21

When I ask for a "Next.js landing page" I get everything crammed into one giant app/page.tsx with inline styles and no separation. Fine for a demo, painful the moment you want to edit the pricing section.

I started specifying the App Router file layout, a components directory with one file per section, and a single source of truth for site content (headline, features, plans) as typed data so copy lives in one place. The model follows a concrete file tree far better than "organize it well".

Still iterating, but it's night and day. Posting the prompt. Anyone have a clean convention for where marketing copy should live in an App Router project?

THE PROMPT
Scaffold a Next.js 14 App Router marketing site (TypeScript, no CSS framework beyond CSS modules). Product: {PRODUCT}. Sections: hero, logos, features, pricing, FAQ, footer CTA.

OUTPUT A FILE TREE FIRST, then each file. Required structure:
- app/(marketing)/page.tsx : composes section components only; contains NO markup beyond <Section/> wrappers.
- components/sections/{Hero,Logos,Features,Pricing,Faq,FooterCta}.tsx : one file per section, each a server component unless it needs interactivity (mark those 'use client' and say why).
- content/site.ts : a single typed export SITE = { hero, features[], plans[], faq[] } - ALL user-facing copy lives here, components read from it. No hardcoded strings in JSX.
- components/ui/{Button,Section,Container}.tsx : shared primitives.
RULES: server components by default; images via next/image with width/height; each section wrapped in a <Section> with a stable id for anchor links; metadata exported from page.tsx. Strict TS, no `any`.
Write real copy in content/site.ts for the described product (3-4 features, 3 plans, 4 FAQs). End with a one-paragraph note on what to edit to change copy vs layout.

1 Answer

6

Centralizing copy in content/site.ts is the right call and I'd push it one step further: make SITE as const and derive your component prop types from it with typeof. Then when a non-technical teammate edits copy they physically cannot break the shape, and if they add a plan the pricing grid types update for free. It also makes the content file the natural handoff artifact to a CMS later.

THE PROMPT
Declare SITE `as const` in content/site.ts and derive component prop types via `type Plan = typeof SITE.plans[number]`. Components import these derived types rather than defining their own.

Your Answer