src/components/blocks/. Each block owns its layout, pulls its copy from
messages/{en,zh}.json, and is independently re-orderable. Composition lives
in a single file: src/app/[locale]/(marketing)/page.tsx.
Prerequisites
None. The blocks are already wired in.The block lineup
Current order inpage.tsx:
src/components/blocks/. None of them take
props — all variation is via i18n keys.
How a block reads its copy
Every block callsuseTranslations with a Home.<block> namespace:
messages/en.json under Home.hero, and the same
keys in messages/zh.json. Editing copy is editing JSON — never touch
component source for words.
Re-order or remove blocks
Opensrc/app/[locale]/(marketing)/page.tsx and rearrange the JSX. To drop a
block, delete the line. To move pricing above features, swap the order. There
is no nav data structure — visual order is JSX order.
Add a new block
- Create
src/components/blocks/<my-block>.tsx. Default-export a server component that callsuseTranslations('Home.myBlock'). - Add
Home.myBlockto bothmessages/en.jsonandmessages/zh.json. - Import and render in
page.tsxat the desired position. - Run
node scripts/check-i18n.mjsto confirm key parity.
Theme tokens
Brand colors and radii are CSS variables defined in the@theme block of
src/app/globals.css (Tailwind v4 directive). Change --primary once and
every block re-skins. Dark mode variables live under .dark further down.
Verify it works
Common pitfalls
- Hardcoded English in components — always use
useTranslations. If you write a literal string, the Chinese site shows English. The reviewer will catch it but you’ll already have wasted commits. - Block order matters for visual rhythm — alternate dense and breathing sections (e.g. Features then Quickstart, not two grids in a row).
- Mobile responsiveness at 360px — the iPhone SE width is the floor. Test it. Hero subtitles love to overflow on narrow screens.
- Adding keys to
en.jsononly — the validator blocks the build. Always add to both locale files. - Importing
next/linkdirectly — useLinkfrom@/i18n/navigationso locale prefixes are preserved on click.
Official docs
- tailwindcss.com — utility classes and the v4
@themedirective - ui.shadcn.com — the component primitives blocks use
- Lucide icons — icon set imported throughout the blocks
- next-intl useTranslations — i18n inside blocks