Content Editing
This is the page you’ll come back to most. Almost every copy change on the site is a one-file edit — you rarely need to touch a component.
The pattern
Each route has:
- A content file in
content/*.tsx— a single typed object exporting all the copy, images, and links for that page. - A thin
page.tsx— imports the content object, spreads its fields as props into organism components, and wraps them in<SiteShell>.
Shared shapes (ImageAsset, StatCard, Testimonial, PricingTier, etc.) live in
content/types.tsx and are reused across content files.
For example, content/home.tsx exports homeContent, an object with keys like hero,
mandate, whyChooseUs, applications, testimonials, faq, and finalCta — one key
per page section. app/home/page.tsx does nothing but read that object and pass each
section’s slice into the matching organism:
export default function Page() {
return (
<SiteShell footerVariant="full">
<HeroSection2 {...homeContent.hero} />
<StepsSection {...homeContent.mandate} />
<StatsSection {...homeContent.whyChooseUs} />
<PinnedShowcaseSection {...homeContent.applications} />
<CarouselSection {...homeContent.testimonials} />
<AccordionSection {...homeContent.faq} />
<CtaBanner heading={homeContent.finalCta.heading} body={homeContent.finalCta.body} ... />
</SiteShell>
);
}Worked example: change the home page headline
The hero heading lives in content/home.tsx, under hero.heading:
hero: {
// ...
heading: "Ship More. Profit always",
body: "Use customer data to build great and solid product experiences that convert.",
primaryCtaLabel: "Start a free trial",
// ...
}Change the string, save, and the home page (app/home/page.tsx) picks it up — no
component edit required. The same applies to every other section key (mandate.heading,
testimonials.slides[n].quote, faq.items[n].content, and so on).
Worked example: add a brand-new route
To add a page that doesn’t exist yet (say, /careers):
-
Define the shape — add any new field types to
content/types.tsxif the page needs something not already modeled (most pages can reuse existing shapes likeHeroContent,StatCard,AccordionItem). -
Create the content file —
content/careers.tsx, exporting a typed object the same wayhome.tsxdoes, including ameta: { title, description }block forgenerateMetadata/staticmetadata. -
Create the route —
app/careers/page.tsx:app/careers/page.tsximport type { Metadata } from "next"; import { SiteShell } from "@/components/templates/site-shell"; import { HeroSection2 } from "@/components/organisms/hero-section-2"; import { careersContent } from "@/content/careers"; export const metadata: Metadata = { title: careersContent.meta.title, description: careersContent.meta.description, }; export default function Page() { return ( <SiteShell footerVariant="full"> <HeroSection2 {...careersContent.hero} /> {/* compose whichever organisms this page needs */} </SiteShell> ); } -
Pick a
footerVariant—"full"for real site pages;"minimal"is reserved for the template’s own showcase page (see Component Architecture for what each does). -
Add a nav entry if the page should appear in the header/footer — see the
navblock incontent/site-wide.tsx.
Compose existing organisms wherever possible before reaching for a new component — see Component Architecture for the full inventory and when it’s actually appropriate to add something new.
Icons in content files
Where a content field needs an icon (e.g. about.tsx’s culture items, case-studies.tsx’s
industry list), import the icon directly from lucide-react and assign the component itself
— not a name string:
import { MapPin, type LucideIcon } from "lucide-react";
export interface CultureItem {
icon: LucideIcon;
title: string;
body: string;
}
items: [
{ icon: MapPin, title: "Remote-First", body: "..." },
// ...
] as CultureItem[],The components that render these (IconFeatureCard, NumberedListItem,
FeatureListSection) take the icon as a component prop and render it directly (e.g.
<item.icon />) — there’s no name-to-component lookup to keep in sync. The one exception is
case-study deliverables sourced from MDX frontmatter, which can’t hold a JS import; see
Writing Articles for how those are resolved.
app/page.tsx vs app/home/page.tsx — don’t mix these up
This is the single most common point of confusion in the codebase:
app/page.tsxis the template’s own showcase/demo landing page — it usescontent/showcase.tsxand is what you see when you first browse the repo. It is not meant to become your production home page.app/home/page.tsxis the actual marketing home page for the site you’re building from this template — it usescontent/home.tsx.
When someone asks you to edit “the home page,” confirm which one they mean before editing.
For a real launch, you’ll typically end up either replacing app/page.tsx’s content with
app/home/page.tsx’s, or redirecting / to /home, depending on how you want the site
structured.