Skip to content

Pages and sections

A page is a small file that picks sections and hands them content. src/app/services/page.tsx is the whole Services page:

export const metadata: Metadata = pageMeta(routes.services, servicesPage.meta);
export default async function ServicesPage() {
const services = await getServices();
return <ServiceCatalogSection content={servicesPage.hero} services={services} />;
}
  • pageMeta(route, meta) gives the page its browser title, description, canonical link and share URL.
  • getServices() reads the collection (see Editing the content).
  • The section renders it.

In src/components/sections/. Each takes its copy as content and its data as a list.

Section Used on
HomeHeroSection, AboutSection, ServicesSection, ProjectsSection, TestimonialsSection, BlogSection, CtaSection Home
AboutHeroSection, ValuesSection, PageImageSection, TeamSection About
ServiceCatalogSection, ServiceHeroSection, ServiceContentSection, MoreServicesSection Services
ProjectGallerySection, ProjectHeroSection, ProjectContentSection, NextProjectSection Projects
BlogArchiveSection, PostHeroSection, PostContentSection, MoreBlogSection Blog
CareerCatalogSection, CareerDetailSection, MoreOpeningsSection Careers
ContactSection Contact
NotFoundSection 404

Move the lines in the page file, or delete one. To take the testimonials off the home page, delete <TestimonialsSection … /> from src/app/page.tsx; its copy in src/content/pages/home.ts can stay or go.

  1. Add the URL to src/content/routes.ts:
    pricing: "/pricing",
  2. Add its copy to src/content/pages/pricing.ts, with a meta (title, description).
  3. Create src/app/pricing/page.tsx:
    import type { Metadata } from "next";
    import { CtaSection } from "@/components/sections/CtaSection";
    import { pricing } from "@/content/pages/pricing";
    import { routes } from "@/content/routes";
    import { pageMeta } from "@/lib/meta";
    export const metadata: Metadata = pageMeta(routes.pricing, pricing.meta);
    export default function PricingPage() {
    return <CtaSection content={pricing.cta} />;
    }
  4. Link to it: add { label: "Pricing", href: routes.pricing } to navigation in src/content/site.ts, or to the footer links.
  5. Add it to src/app/sitemap.ts, next to the other top-level pages.

Delete its folder under src/app/, its entry in navigation and in src/app/sitemap.ts, and the route in routes.ts. npm run build then tells you about anything still linking to it.

src/app/blog/[slug]/page.tsx builds one page per post:

export const dynamicParams = false; // only the slugs below exist; anything else is a 404
export async function generateStaticParams() {
return (await getPosts()).map((post) => ({ slug: post.slug }));
}

Add a post to src/content/collections/posts.ts and its page, its links and its sitemap entry appear on the next build. The same pattern builds the project, service and career pages.

src/app/not-found.tsx, with its copy in src/content/pages/not-found.ts. It is noindex, like the style guide.

  • src/app/sitemap.ts builds sitemap.xml from routes.ts and the collections, so new items list themselves.
  • src/app/robots.ts builds robots.txt and points at the sitemap. Both use site.url, so set your domain before publishing.
  • Every indexable page gets a canonical link and an og:url from pageMeta.