Guide 4 of 4

Developer notes

Optional — only if you want to edit code

Ignore this file if you only want to publish your portfolio — see install.md.

Local development

npm install
npm run setup   # choose "2) Set up local development"
npm run dev

npm run setup has two options. 1) Publish my portfolio deploys to Vercel — that's the normal install path, covered in install.md. 2) Set up local development is the one you want here: it logs you into Convex, creates a dev deployment, sets SITE_URL and BETTER_AUTH_SECRET on it, and writes .env.local.

Prefer to do it by hand? Copy .env.local.example to .env.local, then:

npx convex dev     # writes NEXT_PUBLIC_CONVEX_URL for you
npm run dev

Your Convex development deployment also needs SITE_URL and BETTER_AUTH_SECRET set, or auth won't initialise:

npx convex env set SITE_URL http://localhost:3000
npx convex env set BETTER_AUTH_SECRET "$(openssl rand -base64 32)"

The secret must match the BETTER_AUTH_SECRET in .env.local.

Main scripts

ScriptWhat it does
npm run devLocal Next.js dev server
npm run setupSetup wizard — publish, or configure local dev
npm run buildnext build only
npm run build:deployBootstrap Convex env + convex deploy --cmd 'npm run build' — what production does
npm run deploy:convexDeploy Convex functions only
npm run package:releaseBuild the buyer ZIP
npm run lintESLint

How the production build works

vercel.json overrides Vercel's build command:

node scripts/bootstrap-convex-env.mjs && npx convex deploy --cmd 'npm run build'

Two things to understand:

1. --cmd matters. npx convex deploy --cmd 'npm run build' wraps the frontend build, which is what lets Convex inject NEXT_PUBLIC_CONVEX_URL (and NEXT_PUBLIC_CONVEX_SITE_URL) into it, derived from CONVEX_DEPLOY_KEY. The older convex deploy && next build form chains them instead of nesting them, so no injection happens and the URL has to be supplied by hand. Don't change it back.

2. The bootstrap script runs first. scripts/bootstrap-convex-env.mjs idempotently sets SITE_URL and generates BETTER_AUTH_SECRET on whichever Convex deployment that build's CONVEX_DEPLOY_KEY targets, if they're missing. That's the shared production deployment on a Production build, but a Preview build (any branch push once the repo is git-linked) gets its own separate Convex deployment with its own key — passing --prod there would target the wrong deployment and fail on a permission error, so the script lets the key pick the target instead. It no-ops on every later deploy, no-ops when there's no CONVEX_DEPLOY_KEY (i.e. locally), and never fails the build.

next.config.ts derives NEXT_PUBLIC_CONVEX_SITE_URL from the cloud URL and NEXT_PUBLIC_SITE_URL from VERCEL_PROJECT_PRODUCTION_URL on a Production build, or VERCEL_URL (that deployment's own address) on a Preview build — otherwise a preview would embed the production domain while its Convex deployment expects the preview's own origin, and Better Auth would reject it. Explicit environment variables always win over every derivation above — that's what makes custom domains and local dev work unchanged.

Feature flags

studio and carbon templates are gated behind Vercel Flags (studio-template-enabled, carbon-template-enabled). They stay hidden when Flags isn't configured, so self-hosted installs are safe by default. Run vercel env pull to preview them locally.