Skip to content
Sign inJoin waitlist

Pages and sections — Titen Astro

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

---
import BlogListSection from "../components/sections/BlogListSection.astro";
import blog from "../content/pages/blog.json";
import Layout from "../layouts/Layout.astro";
import { getPosts } from "../lib/content";
import { toPostCard } from "../lib/to-ui";
const posts = await getPosts();
---
<Layout title={blog.blogPage.meta.title} description={blog.blogPage.meta.description}>
<BlogListSection label={blog.blogPage.label} title={blog.blogPage.title} posts={posts.map(toPostCard)} />
</Layout>
  • Layout gives the page its browser title, description, canonical link, share image, header and footer.
  • 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/, one file per section. Each takes its copy from a page file (or site.json) and its items from a collection.

Section Used on
HomeHeroSection, HomeAboutSection, HomeProjectsSection, HomeServicesSection, CtaSection, HomeBlogSection Home
AboutHeroSection, BeginningsSection, ApproachSection, VideoSection, TeamSection, AwardsSection About
ProjectsListSection Projects
CategorySection Residential, Hospitality, Commercial
ProjectHeroSection, ProjectStorySection, RelatedProjectsSection Project
ServicesListSection, ProjectsCtaSection Services
ServiceMainSection Service
BlogListSection Blog
PostMainSection, MorePostsSection Blog post
TestimonialsListSection, ServicesOverviewSection Testimonials
ContactMainSection, ContactSalesSection Contact
ContactFormSection Projects, the category pages, Service
NotFoundSection 404

ContactFormSection (“Let’s Work Together”) appears on five kinds of page with the same copy (projectForm in src/content/site.json), 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.astro, copy in footer.cta).

ProjectMosaic.astro and ProjectGrid.astro are the two project layouts the sections share: the mosaic of six cards (Home, Projects) and the three-column grid. project/ holds the parts of the project story.

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/*.json) and items (src/content/collections/*.json) come from. Header and footer take their links and details from src/content/site.json. 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.json (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.json; the headings and the brochure button from src/content/pages/service.json.

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/pages/index.astro (and its import line); its copy in src/content/pages/home.json can stay or go.

  1. Add the URL to src/content/routes.ts:

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

    {
    "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/pages/careers.astro:

    ---
    import ContactFormSection from "../components/sections/ContactFormSection.astro";
    import careersPage from "../content/pages/careers.json";
    import siteContent from "../content/site.json";
    import PageTitle from "../design-system/components/layout/PageTitle.astro";
    import Section from "../design-system/components/layout/Section.astro";
    import Layout from "../layouts/Layout.astro";
    const { projectForm } = siteContent;
    ---
    <Layout title={careersPage.meta.title} description={careersPage.meta.description}>
    <Section>
    <PageTitle label={careersPage.label} title={careersPage.title} />
    </Section>
    <ContactFormSection {...projectForm} subject="New job application" />
    </Layout>
  4. Link to it: add { "label": "Careers", "href": "/careers" } to navigation in src/content/site.json, or to the footer’s studio.links.

  5. Add it to src/pages/sitemap.xml.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 file under src/pages/, its entry in navigation, in the footer’s links and in src/pages/sitemap.xml.ts, and the route in routes.ts. npm run check then tells you about anything still pointing at it through routes.

src/pages/blog/[slug].astro builds one page per post:

export const getStaticPaths = (async () => {
return (await getPosts()).map((post) => ({ params: { slug: post.slug }, props: { post } }));
}) satisfies GetStaticPaths;

Add a post to src/content/collections/posts.json, and its page, its links and its sitemap entry appear on the next build; any other slug is a 404. The same pattern builds the project, service and category pages (src/pages/[category].astro).

src/pages/404.astro, with its copy in src/content/pages/not-found.json. It is noindex, like the style guide. Static hosts serve 404.html for any unknown address.

  • Sitemap: src/pages/sitemap.xml.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/pages/robots.txt.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 src/layouts/Layout.astro. 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/layouts/Layout.astro from getProjects() and written into each page, so the search also works on static hosts. To search posts too, add them to searchItems there with title, href and text.