Skip to content
Operate And Ship

Going To Production

Pre-launch checklist for product-specific work (brand, copy, legal, live Stripe, email, OAuth). Infrastructure is wired; each category links to its subsystem page rather than duplicating it.

Last updated on

12 min read

On This Page

At A Glance

CategoryTouch this
Branding and identitypackages/brand/src/index.ts, packages/ui/src/components/logo.tsx, apps/web/app/opengraph-image.tsx
Marketing copyapps/web/messages/en.json (and every other locale you ship)
Marketing structureapps/web/config/marketing.ts
Legal pagesapps/web/content/legal/{en,de}/{privacy,terms,license}.mdx (driven by brand.legal in packages/brand/src/index.ts)
Pricing and planspackages/payments/src/catalog/declaration.ts, plus Stripe live-mode products, prices, and webhook
Email senderVerified domain, EMAIL_FROM, provider API key, CONTACT_FORM_TO_EMAIL
Internationalizationpackages/i18n/src/config.ts and apps/web/messages/<locale>.json for every locale you ship
SEO and metadatabrand.seo in packages/brand/src/index.ts, apps/web/app/sitemap.ts, apps/web/app/robots.ts
Operational envSecurity pre-launch checklist, Deployment

Categories can run in any order. Stripe live mode is a short sequence inside its section. The smoke test is the gate before you open to users.

Branding And Identity

Edit the brand object in packages/brand/src/index.ts once. Site metadata, the OG image, email templates, legal MDX placeholders, and the Logo wordmark all read from it.

Update the brand config

In packages/brand/src/index.ts, set name, description, wordmark.primary / wordmark.accent, urls.* (app, docs, pricing), social.*, the full legal.* block (entity, address, governing law, contact and DPO emails, EU representative, arbitration body, currency, payment grace days, effective date), and seo.keywords / seo.author.

Replace the wordmark

packages/ui/src/components/logo.tsx reads brand.wordmark.primary and brand.wordmark.accent. Change those fields for text. Set accent to "" for a monochrome wordmark. For an SVG or <Image>, edit the JSX in Logo. Headers, footers, sidebars, and auth screens pick it up automatically.

Verify the OG image

apps/web/app/opengraph-image.tsx and social-image.tsx read brand.name, brand.wordmark.*, and brand.description. Most rebrands need no generator edits. Change the generator only if you want custom artwork.

Replace the favicon and icons

The kit ships apps/web/app/icon.svg and a generated apple icon (apps/web/app/apple-icon.tsx, mark from @syntaxkit/ui). Replace those with your mark, or drop in static icon.png / apple-icon.png and remove the generated routes. Optional: add apps/web/app/manifest.ts. Next.js discovers these with no extra config.

Marketing Copy And Structure

Put your strings in apps/web/messages/en.json (shape is type-checked; wording is yours). Then adjust layout in apps/web/config/marketing.ts:

marketing.ts fieldControls
sectionsWhich homepage sections render
heroConfig.primaryCta / secondaryCtaSignup and contact destinations
logosBuilt-with trust bar
featuresIcon feature grid (ids map to message keys)
featureHighlightsAlternating image + text rows (imageSrc under apps/web/public/images/)
faqIdsWhich FAQ items render
footerLinksFooter destinations
socialLinksX and GitHub (or remove either)
Overwrite every customer-facing string

Replace the kit copy in apps/web/messages/en.json. apps/web/global.d.ts types messages from that file, so missing keys fail the build.

Translate every locale you ship

Repeat for every other locale (the kit ships en and de). See Internationalization for the locale registry.

The shipped strings still sound like a starter kit ("production-ready", "ship faster"). Read every key in apps/web/messages/en.json before launch. That file owns copy; marketing.ts owns structure.

Legal MDX interpolates brand.legal values (entity, address, governingLaw, contact and DPO emails, EU representative, arbitration body, and related fields) into realistic-looking placeholders. They are not counsel-reviewed. Update the config and have qualified counsel review the templates before a public launch.

Update the brand legal block

Replace every value under brand.legal in packages/brand/src/index.ts with your registered entity. Values flow into /privacy, /terms, and /license in every locale through <Entity />, <Address />, <Jurisdiction />, <ContactEmail />, <DpoEmail />, <EuRepresentative />, <ArbitrationBody />, <Currency />, and <PaymentGraceDays /> in apps/web/content/legal/{en,de}/.

Tailor the legal MDX bodies

The templates are not jurisdiction-specific. Edit apps/web/content/legal/{en,de}/{privacy,terms,license}.mdx for your processing, subprocessors, retention, dispute resolution, and payment terms. Have counsel review before real users see them.

Decide what to do about the license page

/license ships in both locales (apps/web/app/[locale]/(marketing)/license/page.tsx). If you do not license redistributable IP, remove the license entry from footerLinks in apps/web/config/marketing.ts, then delete apps/web/app/[locale]/(marketing)/license/ and the matching MDX files.

Add a cookie banner if you process EU traffic

The kit ships consent helpers (optOut, optIn, hasOptedOut from @syntaxkit/analytics/client) but no banner UI. See Analytics and wire the banner to your CMP.

Pricing And Stripe Live Mode

Work this section top to bottom in the Stripe Dashboard and your env.

Create live-mode products and prices

Switch to live mode (top-right toggle). Create one product each for Pro and Ultra, with monthly and yearly prices in your production currency. Free is the catalog baseline and needs no Stripe price.

Set the price-id env vars

Copy each live price_* id into:

  • STRIPE_PRICE_ID_PRO_MONTHLY
  • STRIPE_PRICE_ID_PRO_YEARLY
  • STRIPE_PRICE_ID_ULTRA_MONTHLY
  • STRIPE_PRICE_ID_ULTRA_YEARLY

The billing catalog derives required vars from packages/payments/src/catalog/declaration.ts. The app fails at startup if any declared price env is missing.

Update the billing catalog to match Stripe

In packages/payments/src/catalog/declaration.ts, update each paid plan (pro, ultra): name, description, entitlements, trialDays, and displayed amount / currency per price. Feature bullets come from entitlements (and BillingFeatures messages), not a separate features list. Stripe owns billed amounts; a stale catalog amount makes the pricing page lie.

Verify the catalog matches Stripe

Run pnpm billing:check-prices with your Stripe keys and STRIPE_PRICE_ID_* set. It fails on amount or currency drift, archived Prices, or missing Prices (zero-decimal currencies like JPY handled correctly). The scheduled stripe-live CI workflow runs the same check. It is a build/dev-time guard only (no Stripe call on render paths) and skips cleanly when billing is not configured.

Register the production webhook

Add an endpoint at <NEXT_PUBLIC_APP_URL>/api/webhooks/stripe. Subscribe to the nine events on Webhooks And Async Workflows: checkout.session.completed, customer.subscription.{created, updated, deleted, trial_will_end}, invoice.{finalized, payment_action_required, payment_failed, payment_succeeded}.

Set the production secrets

Put the production webhook signing secret (not the Stripe CLI one) in STRIPE_WEBHOOK_SECRET, and your sk_live_* key in STRIPE_SECRET_KEY. Both are runtime env, not build args.

Enable the payment methods you want

In Settings → Payment methods, enable cards plus any wallets (Apple Pay, Google Pay, Link), Buy Now Pay Later, and regional methods (SEPA, iDEAL, Bancontact, and similar). The kit does not pin payment_method_types, so Dashboard toggles apply to new Checkout sessions without a redeploy.

Activate Stripe Tax (if you need VAT, GST, or sales tax)

In Settings → Tax, activate Stripe Tax and add a tax registration for each obligation. Then set STRIPE_AUTOMATIC_TAX="true" in runtime env. Checkout collects billing addresses, shows a VAT / tax-id field for B2B buyers, and computes tax. Leave the flag unset until at least one registration exists, or Stripe rejects every Checkout session.

The pricing table reads the catalog. Webhooks drive subscription state. Keep them aligned with pnpm billing:check-prices and the stripe-live CI job so the site never shows one price while Stripe charges another.

Email Sender Configuration

Point the kit at your provider, set the env vars, and smoke-test every template before launch.

Verify your sending domain

In your provider dashboard (Plunk, Resend, or whichever mode you use), verify the sending domain. Add DKIM and SPF DNS records, plus DMARC when you can. Without them, Gmail and Outlook deliverability drops sharply.

Set EMAIL_FROM

Set EMAIL_FROM to a verified address or Name <address@yourdomain.com> string. sendEmail passes this from value to the active provider. The address or domain must be verified with that provider. To change providers, see Email and Setup.

Set the runtime env vars

Set the production provider key (for Plunk: PLUNK_API_KEY), EMAIL_FROM, and CONTACT_FORM_TO_EMAIL (where public contact-form submissions land). Production rejects EMAIL_DELIVERY_MODE=log and noop at boot.

Smoke-test every template

From staging, trigger one of each: verification, password reset, organization invitation, subscription created, payment failed. Open each in the recipient inbox and confirm rendering, sender display, and deliverability before real users.

Internationalization

Pick locales, translate every key, and decide what search engines can crawl.

Decide which locales you ship

Edit packages/i18n/src/config.ts (locales and localeLabels). Defaults are en and de. Trim or add as needed.

Translate every key per locale

For every locale you keep, apps/web/messages/<locale>.json must include every key from en.json. The TypeScript build catches missing keys.

Audit indexability

apps/web/proxy.ts redirects unprefixed marketing routes to the preferred locale via the NEXT_LOCALE cookie. Remove a locale from the registry if you do not want it crawled. Full walkthrough: Internationalization.

SEO And Metadata

Swap starter-kit metadata for yours, then register the sitemap.

Replace SEO keywords and author

Update brand.seo.keywords and brand.seo.author in packages/brand/src/index.ts. apps/web/app/layout.tsx reads those defaults. Replace starter-kit terms ("SaaS starter kit", "Next.js", "boilerplate") with terms for your product.

Extend the sitemap

Add new marketing routes to the staticRoutes array in apps/web/app/sitemap.ts. The helper emits locale-aware entries with the right alternates.languages block.

Audit robots.txt

The default disallow list in apps/web/app/robots.ts covers /auth/, /dashboard/, /api/, /rpc/, /api-reference/, /accept-invitation/, /create-organization/, plus locale-prefixed auth and onboarding paths. That is correct for almost every SaaS. Change it only if your product has a non-standard public surface.

Verify the canonical app URL

Confirm NEXT_PUBLIC_APP_URL matches the canonical production origin. CSP origins, CORS allow-origin, sitemap entries, and OG image URLs all derive from it.

Submit the sitemap

Submit <NEXT_PUBLIC_APP_URL>/sitemap.xml to Google Search Console and any other search engines that matter to you.

Operational Environment Variables

The full env-var matrix lives on Environment Variables. Production-specific concerns each have one home:

ConcernWhere the checklist lives
Auth secrets, abuse protection, captcha, multi-instance encryption keySecurity pre-launch checklist
Per-host build-arg vs runtime env splitDeployment build-time vs runtime env
Storage bucket configuration (CORS, lifecycle rules)Storage required bucket configuration
Source map upload secretsMonitoring source maps
OAuth callback URLs in GitHub / Google consolesSecurity pre-launch checklist

Pre-Launch Checklist

Work the categories above, then tick them off here. Progress saves to this browser's localStorage.

Before You Deploy

Smoke Test After First Deploy

Run this against the live production deploy. Anything that fails goes back to its subsystem page before you open to users. Progress persists in this browser.

After Your First Deploy

Where To Go Next

Also useful: Setup for the env-var matrix, Webhooks And Async Workflows for the Stripe events table, and Monitoring for source-map upload and post-launch visibility.

Was this page helpful?

On this page