Skip to content
Sign inJoin waitlist

Pages and sections — Titen Next.js

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

export const metadata = pageMeta(routes.blog, blogPage.meta);
export default async function BlogPage() {
const posts = await getPosts();
return <BlogListSection label={blogPage.label} title={blogPage.title} posts={posts.map(toPostCard)} />;
}
  • pageMeta(route, meta) gives the page its browser title, description, canonical link and share image.
  • getPosts() reads the collection (see Editing the content). toPostCard turns each post into what the card shows (src/lib/to-ui.ts).
  • The section renders them.

In src/components/sections/. Each takes its copy from a page file (or site.ts) and its items from a collection. Sections that belong to one page share a file: AboutSections.tsx holds every About section.

Section File Used on
HomeHeroSection, HomeAboutSection, HomeProjectsSection, HomeServicesSection, CtaSection, HomeBlogSection one file each Home
AboutHeroSection, BeginningsSection, ApproachSection, VideoSection, TeamSection, AwardsSection AboutSections.tsx About
ProjectsListSection ProjectsListSection.tsx Projects
CategorySection CategorySection.tsx Residential, Hospitality, Commercial
ProjectHeroSection, ProjectStorySection, RelatedProjectsSection ProjectSections.tsx Project
ServicesListSection, ProjectsCtaSection ServicesSections.tsx Services
ServiceMainSection ServiceMainSection.tsx Service
BlogListSection, PostMainSection, MorePostsSection BlogSections.tsx Blog, blog post
TestimonialsListSection, ServicesOverviewSection TestimonialsSections.tsx Testimonials
ContactMainSection, ContactSalesSection ContactSections.tsx Contact
ContactFormSection ContactFormSection.tsx Projects, the category pages, Service
NotFoundSection NotFoundSection.tsx 404

ContactFormSection (“Let’s Work Together”) appears on five kinds of page with the same copy (projectForm in src/content/site.ts), so changing it once changes it everywhere. The dark “Let’s Talk!” block above every footer is part of the footer (src/components/site/Footer.tsx, copy in footer.cta).

ProjectMosaic.tsx holds the two project layouts the sections share: the mosaic of six cards (Home, Projects) and the three-column grid.

Every page type with each section outlined and labelled: the component that renders it, the file to edit, and where its copy (src/content/pages/*.ts) and items (src/content/collections/*.ts) come from. Header and footer take their links and details from src/content/site.ts. Photos are the placeholders of the download.

Home: sections and the files behind them

The team and the awards are in src/content/pages/about.ts (team.members, awards.items).

About: sections and the files behind them

Projects: sections and the files behind them

The page’s title is the category’s name; its projects are the ones whose category is its slug, the first one shown wide.

A category page: sections and the files behind them

Project: sections and the files behind them

Services: sections and the files behind them

The title, image, “About the service” and “What’s included” come from the service in services.ts; the headings and the brochure button from src/content/pages/service.ts.

Service: sections and the files behind them

Blog: sections and the files behind them

Blog post: sections and the files behind them

Testimonials: sections and the files behind them

Contact: sections and the files behind them

Page not found: sections and the files behind them

Move the lines in the page file, or delete one. To take the blog off the home page, delete <HomeBlogSection … /> 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:

    careers: "/careers",
  2. Add its copy to src/content/pages/careers.ts, with a meta (title, description):

    export const careersPage = {
    meta: { title: "Careers", description: "Join the Titen studio: open roles in architecture, interiors and project management." },
    label: "Careers",
    title: "Build the future with us",
    };
  3. Create src/app/careers/page.tsx:

    import { Section, PageTitle } from "@/design-system";
    import { ContactFormSection } from "@/components/sections/ContactFormSection";
    import { careersPage } from "@/content/pages/careers";
    import { routes } from "@/content/routes";
    import { projectForm } from "@/content/site";
    import { submitContact } from "@/lib/actions";
    import { pageMeta } from "@/lib/meta";
    export const metadata = pageMeta(routes.careers, careersPage.meta);
    export default function CareersPage() {
    return (
    <>
    <Section>
    <PageTitle label={careersPage.label} title={careersPage.title} />
    </Section>
    <ContactFormSection {...projectForm} action={submitContact} />
    </>
    );
    }
  4. Link to it: add { label: "Careers", href: routes.careers } to navigation in src/content/site.ts, or to the footer’s studio.links.

  5. Add it to src/app/sitemap.ts, next to the other top-level pages.

Category pages live at the top level (/residential), so a new page must not share its URL with a category’s slug.

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

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 category pages (src/app/[category]/page.tsx).

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

  • Sitemap: src/app/sitemap.ts builds sitemap.xml from routes.ts and the collections, so new items list themselves. The 404 page and the style guide are left out.
  • Robots: src/app/robots.ts builds robots.txt and points at the sitemap. Both use site.url, so set your domain before publishing.
  • Per page: every indexable page gets a title, a description, a canonical link, an og:url and a share image from pageMeta (src/lib/meta.ts). A title that would be longer than 60 characters with | Titen appended is shown without the suffix.
  • Not indexed: the style guide and the 404 page (noindex).

“Search projects” in the header looks through the project pages: titles first, then the text of every project. The list is built in src/app/layout.tsx from getProjects(). To search posts too, add them to searchItems there with title, href and text.