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
| Script | What it does |
|---|---|
npm run dev | Local Next.js dev server |
npm run setup | Setup wizard — publish, or configure local dev |
npm run build | next build only |
npm run build:deploy | Bootstrap Convex env + convex deploy --cmd 'npm run build' — what production does |
npm run deploy:convex | Deploy Convex functions only |
npm run package:release | Build the buyer ZIP |
npm run lint | ESLint |
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.