Skip to content

Conversation

@wizard-ci-bot
Copy link

@wizard-ci-bot wizard-ci-bot bot commented Jan 15, 2026

Automated wizard CI run

Source: manual
Trigger ID: a283762
App: react-router/saas-template
App directory: apps/react-router/saas-template
Workbench branch: wizard-ci-a283762-react-router-saas-template
Wizard branch: main
Examples branch: main
PostHog (MCP) branch: master
Timestamp: 2026-01-15T22:43:08.507Z
Duration: 674.9s

@wizard-ci-bot
Copy link
Author

wizard-ci-bot bot commented Jan 15, 2026

Now I have read all the key changed files. Let me compile my evaluation report.


PR Evaluation Report

Summary

This PR adds PostHog analytics integration to a React Router 7 SaaS template. The implementation includes client-side initialization with PostHogProvider, server-side middleware for context propagation, error boundary exception capture, user identification on auth flows, and comprehensive event tracking across authentication, billing, onboarding, and organization management features.

Files changed Lines added Lines removed
28 +475 -50

Confidence score: 4/5 👍

  • Missing reverse proxy configuration: No server-side proxy is set up to bypass ad blockers, which will reduce data capture rates in production. [MEDIUM]
  • Server-side PostHog middleware is configured but never used: The posthog instance added to context in the middleware is never accessed in any route/loader. [MEDIUM]
  • User identification with email as distinct ID: Using email as the distinct ID in login/signup is problematic for users who change emails or use Google OAuth later. Consider using a stable user ID after successful authentication. [MEDIUM]

File changes

Filename Score Description
app/entry.client.tsx 4/5 PostHog client initialization with PostHogProvider wrapper, uses defaults preset, tracing headers configured
app/root.tsx 4/5 Added PostHog middleware to middleware chain, error boundary captures exceptions via captureException
app/lib/posthog-middleware.server.ts 3/5 New server-side middleware creates PostHog instance per request, but the context value is never consumed in routes
app/routes/_user-authentication+/_anonymous-routes+/login.tsx 4/5 Tracks login_form_submitted with method and invite status, identifies user by email on email login
app/routes/_user-authentication+/_anonymous-routes+/register.tsx 4/5 Tracks signup_form_submitted with method and invite status, identifies user by email on email signup
app/features/billing/cancel-or-modify-subscription-modal-content.tsx 5/5 Tracks subscription_plan_changed with rich context (upgrade/downgrade, tiers, intervals) and subscription_cancellation_initiated
app/features/billing/create-subscription-modal-content.tsx 5/5 Tracks subscription_checkout_started with tier, interval, and lookup key
app/features/billing/contact-sales/contact-sales-team.tsx 4/5 Tracks contact_sales_form_submitted on form submit
app/features/organizations/layout/nav-user.tsx 5/5 Tracks logout_clicked and correctly calls posthog.reset() to clear user identity
app/features/organizations/create-organization/create-organization-form-card.tsx 4/5 Tracks organization_created on form submit
app/features/organizations/accept-email-invite/accept-email-invite-page.tsx 4/5 Tracks email_invite_accepted with organization name
app/features/organizations/settings/team-members/invite-by-email-card.tsx 3/5 Tracks team_member_invited but uses fragile DOM query to get role value
app/features/user-accounts/settings/account/danger-zone.tsx 5/5 Tracks user_account_deleted and correctly calls posthog.reset()
app/routes/_authenticated-routes+/onboarding+/organization.tsx 4/5 Tracks onboarding_organization_completed on form submit
app/routes/_authenticated-routes+/onboarding+/user-account.tsx 4/5 Tracks onboarding_user_account_completed on form submit
app/routes/api+/v1+/stripe.webhooks.ts 4/5 Server-side tracking for stripe_checkout_completed and stripe_subscription_created via Stripe webhooks
vite.config.ts 5/5 Correctly adds SSR noExternal config for posthog-js and @posthog/react
package.json 5/5 Adds posthog-js, posthog-node, and @posthog/react dependencies
app/features/pastebin/paste-helpers.server.ts 5/5 Code cleanup: fixes Infinity to Number.POSITIVE_INFINITY, reorders object properties
Other route/component files 5/5 Minor formatting changes (property ordering, whitespace) - no functional impact

App sanity check: 4/5 ✅

Criteria Result Description
App builds and runs Yes Dependencies added correctly, SSR config updated, no breaking changes
Preserves existing env vars & configs Yes Uses VITE_PUBLIC_ prefixed env vars (existing pattern), existing configs untouched
No syntax or type errors Yes TypeScript types are correct, PostHog hooks imported properly
Correct imports/exports Yes All imports from @posthog/react and posthog-js/posthog-node are valid
Minimal, focused changes Yes Changes are focused on PostHog integration with minimal refactoring

Issues

  • Server middleware unused: The posthog-middleware.server.ts creates a PostHog instance and adds it to context, but no route or loader ever retrieves it. The middleware runs on every request but provides no value currently. [MEDIUM]
  • Fragile DOM query in invite-by-email-card.tsx: Using document.querySelector('button[data-slot="select-trigger"]') to get the role value is fragile and may break if the Select component changes. Should use form data or a ref instead. [LOW]

Other completed criteria

  • Environment variables follow VITE_PUBLIC_ naming convention
  • No PII exposed in events beyond email (which is intentional for identify)
  • Build configuration properly updated with SSR noExternal
  • Consistent with existing React Router patterns
  • Proper cleanup with posthog.reset() on logout and account deletion

PostHog implementation: 4/5 ✅

Criteria Result Description
PostHog SDKs installed Yes posthog-js ^1.324.1, posthog-node ^5.21.0, @posthog/react ^1.5.2
PostHog client initialized Yes Initialized in entry.client.tsx with PostHogProvider wrapping the app, uses defaults: "2025-11-30" preset
capture() Yes 15+ custom events captured across auth, billing, onboarding, and org management flows
identify() Partial Called on email login/signup with email as distinct ID; no post-auth identify with stable user ID
Error tracking Yes ErrorBoundary in root.tsx calls posthog?.captureException(error)
Reverse proxy No No reverse proxy configured; events sent directly to PostHog API host

Issues

  • No reverse proxy implementation: The integration sends events directly to PostHog's API host without a reverse proxy. This means ad blockers will likely block a significant portion of analytics data. A proxy route (e.g., /ingest/*) should be configured. [MEDIUM]
  • Server middleware not utilized: While posthog-node is installed and a middleware is created, the server-side PostHog instance in context is never used by any route. Server-side events are only captured in stripe.webhooks.ts by creating new PostHog instances. [MEDIUM]
  • User identification timing: Users are identified by email during form submission before the request completes. If login fails, the user is still identified. Should identify after successful authentication. [LOW]

Other completed criteria

  • API host configured via environment variable
  • Tracing headers configured for localhost and current host
  • PostHog reset() called on logout and account deletion
  • SSR configuration properly handles PostHog packages
  • Server-side PostHog instances properly shut down after use

PostHog insights and events: 4/5 ✅

Filename PostHog events Description
login.tsx login_form_submitted Tracks login attempts with method (email/google) and invite status
register.tsx signup_form_submitted Tracks signup attempts with method and invite status
nav-user.tsx logout_clicked Tracks user logout actions
user-account.tsx onboarding_user_account_completed Tracks completion of user profile onboarding step
organization.tsx onboarding_organization_completed Tracks completion of organization onboarding step
create-organization-form-card.tsx organization_created Tracks new organization creation
create-subscription-modal-content.tsx subscription_checkout_started Tracks checkout initiation with tier, interval, lookup_key
cancel-or-modify-subscription-modal-content.tsx subscription_plan_changed, subscription_cancellation_initiated Tracks plan changes (upgrade/downgrade) and cancellation initiation
contact-sales-team.tsx contact_sales_form_submitted Tracks enterprise sales inquiries
invite-by-email-card.tsx team_member_invited Tracks team invitations with role
accept-email-invite-page.tsx email_invite_accepted Tracks invite acceptance with org name
danger-zone.tsx user_account_deleted Tracks account deletion
stripe.webhooks.ts stripe_checkout_completed, stripe_subscription_created Server-side revenue events from Stripe
root.tsx captureException Captures unhandled errors in error boundary

Issues

  • Missing pageview tracking verification: While PostHog's defaults preset should include automatic pageview tracking, this isn't explicitly verified or documented. Should confirm pageviews are being captured. [LOW]
  • Missing paste/feature usage events: The core app feature (pastebin) has no tracking for paste creation, viewing, or deletion, which would be valuable for product analytics. [LOW]

Other completed criteria

  • Events cover full user lifecycle (signup → onboarding → usage → billing → churn)
  • Billing events include rich context for funnel analysis (tiers, intervals, upgrade/downgrade)
  • Events use consistent naming convention (snake_case)
  • Events enable key insights: conversion funnels, onboarding completion, subscription changes, churn
  • Server-side Stripe events provide accurate revenue tracking independent of client-side blockers

Reviewed by wizard workbench PR evaluator

@wizard-ci-bot wizard-ci-bot bot added the CI/CD label Jan 15, 2026
@wizard-ci-bot wizard-ci-bot bot closed this Jan 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant