The three layers
| Layer | File | What it controls |
|---|---|---|
| Brand & business config | src/config/site.ts | Product name, pricing, plans, providers active flag, social links — anything a buyer should change. |
| Secrets & runtime config | .env.local (validated by src/env.ts) | API keys, database URL, signing secrets — anything that varies per environment. |
| Customer-facing copy | messages/en.json + messages/zh.json | Every string the user sees. Bilingual; both files must have the same keys. |
messages/{en,zh}.json instead.
What siteConfig controls
The whole file is ~250 lines. Here are the load-bearing fields:
What lives outside siteConfig
These have their own homes — by design.
| Concern | Where |
|---|---|
| Env vars / secrets | .env.local, validated by src/env.ts |
| Database schema | src/db/{auth,app,affiliate,ai,license}.schema.ts |
| AI provider list / pricing | src/ai/index.ts + src/ai/pricing.ts |
| Marketing copy | messages/{en,zh}.json |
| Mail templates | src/mail/templates/ |
| Theme tokens | src/app/globals.css (@theme block) |
| Sidebar nav for /docs | src/config/docs-nav.ts |
Adding a new feature flag
-
Add to
siteConfig.features: -
Reference in code:
Adding an env var
- Add a Zod-validated entry in
src/env.ts(server section if it’s secret; client section if it has theNEXT_PUBLIC_prefix and is safe to ship to the browser). - If it’s a public var, add it to
experimental__runtimeEnvtoo (Next.js requires this). - Document it in Env reference.
- Use
import { env } from '@/env'to read it — neverprocess.env.Xdirectly.
Best-practice checklist
- ✅ Brand changes happen in
siteConfigandmessages/. No component-source edits. - ✅ Secrets only in
.env.local. The.env.exampledocuments the shape; the real.env.localis gitignored. - ✅ When swapping a provider (Stripe → Paddle), change
siteConfig.payment.providerAND set the matching*_PRICE_*env vars. The rest of the app doesn’t care which provider is active. - ✅ Both
messages/en.jsonandmessages/zh.jsonalways have the same keys.node scripts/check-i18n.mjsenforces this.