Nextbase Docs
Nextbase Docs
HomeBlogWelcome to Nextbase v5!Choose your starterGetting startedLocal Supabase per agent
TanStack + SupabaseInstallationEnvironment variablesAuthenticationAgent connectionsProject structureDeployment
AuthenticationWorkspaces and the admin area
Building with agentsEmails with Resend
TroubleshootingNextbase v5
The 8 starter kitsTanStack + Supabase

Project structure

The TanStack + Supabase monorepo layout and where to make common changes.

The repository is a pnpm workspace (apps/* and packages/*) orchestrated by Turborepo. Four apps and one shared package.

root.tsx
routes.ts
.env.example
react-router.config.ts
vite.config.ts
playwright.config.ts
vercel.json
index.mjs
.env.example
vercel.json
index.mjs
AGENTS.md
README.md
deployment.md
setup.sh
package.json
pnpm-workspace.yaml
turbo.json

The apps

PathWhat it is
apps/webThe product: React Router 8 framework-mode app (appDirectory: "src") served by Express 5 on port 3000. Public marketing pages, auth, /app/*, the public API routes and the User MCP. In development Express mounts Vite in middleware mode; in production it serves build/client and imports build/server/index.js, bundled to dist/index.js by tsup.
apps/adminThe administration app, same stack, port 3002 locally and its own deployment. Serves /admin/*, admin auth pages, the marketing media upload route and the Admin MCP. Shares the Supabase project with apps/web; WEB_APP_URL points it at the product for support sign-in links. Described in docs/admin-app.md.
apps/databaseThe Supabase project: config.toml (stack engine enabled), hand-written migrations, pgTAP tests, local seed and the generated types exported as database/types. The Supabase CLI 2.121.0 is a dependency here.
apps/emailReact Email template catalogue (emails/*.tsx) with a preview server on port 3001. Not imported by the apps.
packages/typescript-configShared strict tsconfig.base.json.

apps/admin is a trimmed copy of apps/web: the same server/index.ts shape, the same src/.server/ modules (auth.ts, env.ts, integrations.ts, mail.ts, request-context.ts, plus web-app-url.ts), the same src/mcp/ code with a different audience, and only the marketing feature module it needs for content administration. Conventions documented for apps/web below apply to both unless noted.

Where to make changes

Routes and pages

Routes are registered explicitly in apps/web/src/routes.ts and apps/admin/src/routes.ts with index, route and layout. A file in src/routes/ does nothing until it is listed there. Route modules export loader, action, meta and a default component. API resource routes are .ts files named api-*.ts.

In the web app, /app/* pages nest under routes/app-layout.tsx; navigation entries live in src/components/app-shell.tsx (productItems) and src/components/site-header.tsx for public pages. / and /terms are prerendered (react-router.config.ts), so keep their loaders free of per-user data. In the admin app, /admin/* pages nest under routes/admin-layout.tsx and the navigation is in its own src/components/app-shell.tsx. The repository's new-page skill in .agents/skills/ walks through adding a page.

Server and data access

Loaders and actions get RequestServices from context.get(servicesContext) (src/.server/request-context.ts): supabase() for user-scoped reads and writes under RLS, serviceSupabase() only after requireUser or requireAdmin plus an explicit authorization check. Server-only code belongs in src/.server/ or *.server.ts files, which React Router strips from client bundles. Mutations are React Router actions with a hidden intent field, validated with Zod (shared schemas in src/features/core/core-utils.ts), returning { error } or { success }.

Feature data access for marketing content sits in src/features/marketing/*.server.ts (the public reads in the web app, the CMS writes in the admin app). Optional integrations are read through src/.server/integrations.ts; core env validation is in src/.server/env.ts.

Schema and migrations

pnpm --filter database exec supabase migration new <snake_case_name>
pnpm --filter database exec supabase db reset
pnpm gen-types-local
pnpm test-db

Migrations in apps/database/supabase/migrations/ are the only source of truth; supabase/schemas/init.sql is a stale snapshot, so do not diff against it. Never edit a shipped migration. Every new table needs RLS, named policies, explicit grants and indexes. Add pgTAP coverage in supabase/tests/NN_*.sql and commit the regenerated apps/database/lib/database.types.ts. The db-migration skill covers this flow and reads the local database address from supabase status --output-format json.

Authentication

Guards are in src/.server/auth.ts of each app; auth pages are routes/login.tsx, routes/sign-up.tsx (web only), routes/forgot-password.tsx, routes/reset-password.tsx, routes/auth-callback.tsx and routes/auth-confirm.ts. Local Auth settings (confirmations, redirect URLs for both ports, providers) are in apps/database/supabase/config.toml. See authentication.

Emails

App emails are built as HTML and text in src/.server/mail.ts and sent through Resend when RESEND_API_KEY and EMAIL_FROM are set. The templates in apps/email/emails/ are a design catalogue; to send one from an app, add @nextbase/email as a workspace dependency, render it with @react-email/render and pass the HTML to sendTransactionalEmail. Supabase Auth emails are configured in the Supabase project, not here.

Billing and background work

Stripe lives in apps/web/src/.server/billing/ (stripe.ts, payment-gateway.ts) with the webhook route at routes/api-stripe-webhook.ts. There is no separate worker or queue in this kit; the Express servers handle everything, and the Stripe webhook on the web app is the only inbound background trigger. The admin app's /admin/billing reads the same tables.

MCP

Each app's MCP and OAuth server is its src/mcp/, mounted by its server/index.ts before React Router. The web app serves the User MCP, the admin app the Admin MCP. Follow the mcp-tool skill and agent connections.

Local environment

setup.sh (pnpm setup) and scripts/sync-supabase-env.ts (pnpm supabase:sync-env) are the only scripts that touch .env files: the sync script reads env.API_URL, env.ANON_KEY and env.SERVICE_ROLE_KEY from supabase status --output-format json and writes them into apps/web/.env and apps/admin/.env. Because config.toml enables the CLI stack engine, there are no fixed local ports to hard-code anywhere.

Tests

  • Vitest (jsdom) unit tests sit next to code as *.test.ts(x) in apps/web/src and apps/admin/src. Run pnpm test, or one file with pnpm --filter web exec vitest run <file> (or --filter admin).
  • *.integration.test.ts files skip unless the MCP_TEST_SUPABASE_* variables point at a local project.
  • Playwright specs are in apps/web/e2e/; pnpm test:e2e starts the web and admin dev servers unless CI is set, and the seeded-account spec signs in on the admin app.
  • pgTAP tests are in apps/database/supabase/tests/; pnpm test-db needs a running local project.
  • pnpm check runs format:check, lint, typecheck, test and build across all apps. Each app build also runs its scripts/check-built-auth.mjs and scripts/package-vercel.mjs.

AI coding tool configuration

AGENTS.md (root and per app, including apps/admin/AGENTS.md), .agents/config.json and .agents/skills/ are the hand-edited sources. CLAUDE.md, GEMINI.md, .mcp.json, .cursor/, .vscode/mcp.json and the other tool files are generated by pnpm agents:sync; CI runs pnpm agents:check. Edit the sources, not the generated files. The supabase-local MCP server URL in .agents/config.json should be the MCP_URL of your running local project.

Agent connections

The User MCP and Admin MCP in the TanStack + Supabase starter: endpoints, tools, OAuth behaviour and how to extend them.

Deployment

Provision Supabase, apply the migrations and publish the TanStack + Supabase starter's web and admin apps to Vercel or any Node 24 host.

On this page

The appsWhere to make changesRoutes and pagesServer and data accessSchema and migrationsAuthenticationEmailsBilling and background workMCPLocal environmentTestsAI coding tool configuration