Pages
Skill level: Beginner to edit, light code to add a page made of sections
Every page has two parts: a route file in src/pages/[...lang]/, which decides which sections the page shows and in what order, and a content file in src/content/, which holds its title, SEO fields, page header, and named blocks. You edit the content file. You only touch the route to add, remove, or reorder sections.
Every Page And Its File
Paths below are for English. French uses the same files in french/, served under /fr/, and Arabic in arabic/, served under /ar/.
| Page | URL | Route (src/pages/[...lang]/) | Content (src/content/) |
|---|---|---|---|
| Solo designer home | / | index.astro | homepage/english/-index.md |
| Studio home | /home-studio/ | home-studio.astro | homepage/english/home-studio.md |
| Photographer home | /home-photo/ | home-photo.astro | homepage/english/home-photo.md |
| Projects | /projects/ | projects/index.astro | projects/english/-index.md |
| Project or photo series | /projects/<entry>/ | projects/[single].astro | projects/english/<entry>.md |
| Archive | /archive/ | archive.astro | pages/english/archive.mdx |
| Services | /services/ | services/index.astro | services/english/-index.md |
| Service page | /services/<entry>/ | services/[single].astro | services/english/<entry>.md |
| Pricing | /pricing/ | pricing.astro | pricing/english/-index.md |
| FAQ | /faq/ | faq.astro | faq/english/-index.md |
| About | /about/ | about.astro | about-us/english/-index.md |
| Team | /team/, /team/<member>/ | team/ | team/english/ |
| Testimonials | /testimonials/ | testimonials.astro | testimonials/english/-index.md |
| Journal | /blog/, posts, /blog/category/<name>/, /blog/tag/<name>/, /blog/rss.xml | blog/ | blog/english/, authors in author/english/ |
| Contact | /contact/ | contact.astro | contact/english/-index.md |
| Legal and text pages | /privacy-policy/, /terms-conditions/, /cookie-policy/ | [page].astro | pages/english/*.md |
| Shortcode demo | /components/ | [page].astro | pages/english/components.mdx |
| Layout gallery | /elements/ | elements.astro | pages/english/elements.mdx |
| 404 | any missing URL | 404.astro | sections/english/not-found-section.md |
The homepages are explained in Homepages, projects and the archive in Projects, and the other collections in Collections.
Named Blocks
A page file changes the sections it shows with named blocks: pageHeader:, projectsSection:, faqSection:, ctaSection:, and so on. A block is merged over the section’s own file in src/content/sections/<language>/:
- Objects merge key by key, so a block only needs the fields it changes.
- Lists replace the whole list: repeat every item you want to keep.
- An empty string (
"") clears a value.
projectsSection: title: "" # no heading row on this page options: layout: "index" # the other options keep their section defaults order: "year"Copy block names from sectionsSchema in src/sections.schema.ts. A misspelled block name, such as teamSection: instead of teamGridSection:, is ignored without an error. A contactSection block in a page file that has a schema needs title, description, and form. See Sections.
The call to action. Inner pages show the call to action section above the footer. Change it on one page with a ctaSection: block, or hide it there with ctaSection: { enable: false }. The solo designer homepage doesn’t show it.
Page Header
Inner pages start with a page header. It is set by the pageHeader block in the page’s content file. The title is pageHeader.title, or the page’s title when that is empty.
pageHeader: title: "Five ways to *work* together" # one line; *word* is set in the serif accent breadcrumb: false eyebrow: "What I do" # a small pill above the title description: "**From a first mark to a full season of pictures.** Each service starts with a short call." primaryButton: enable: true label: "Start a project" url: "/contact/" secondaryButton: enable: true label: "See pricing" url: "/pricing/" variant: "outline" options: layout: "centered" # centered | left | split titleSize: "display" # display (large) | title (smaller)buttons adds more buttons. In the split layout, image and imageAlt put a portrait on the right, as on /contact/. In description, **bold** text is set darker than the rest of the line. Project, service, team member, journal post, and text pages use the left layout unless their own pageHeader block sets another one. pageHeaderSchema in src/sections.schema.ts lists every field.
Fields Every Page Accepts
These frontmatter fields work on page and entry files. src/content/_file_format.md explains most of them; the full list is the page schema in src/content.config.ts.
| Field | What it does |
|---|---|
title, description | The page title and summary |
metaTitle, metaDescription | Replace the title and description in search results only |
image | The cover or social preview image of the page |
date, updated | Publish date and last edit. Text pages show “Last updated” |
draft | true leaves the page out of the built site. It still shows in npm run dev |
customSlug | A different URL segment than the file name. Use the same value in every language |
canonical, robots, keywords | SEO overrides |
excludeFromSitemap, excludeFromSearch, excludeFromAi | Leave the page out of the sitemap, site search, or the AI files |
disableTagline | Don’t add the site tagline to this page’s title |
headerLayout | Another header layout on this page: pill, bar, or overlay |
footerLayout | Another footer layout on this page: wordmark, columns, or email |
contactDock | true or false shows or hides the contact dock on this page. See Contact & Booking |
draft: true in a collection’s -index.md file leaves out the list page and every entry of that collection.
Text Pages
Every file in src/content/pages/<language>/ becomes a page at /<file name>/, rendered by src/pages/[...lang]/[page].astro, except archive.mdx and elements.mdx, which have their own route files. The legal pages are built this way.
title: "Privacy policy"description: "What I collect when you write to me..." # the line under the titledate: 2026-10-01 # shown as "Last updated" (or set `updated`)pageHeader: options: layout: "left"contentPage: notice: "**A template to adapt, not legal advice.** ..." # a note above the textThe Markdown body is the page text. Each ## heading joins the table of contents beside the text (above it on phones), when the page has two or more. contentPage.notice is a highlighted note above the text, in Markdown.
Add A Page
For policies, guides, or any page of text.
-
Copy
src/content/pages/english/terms-conditions.mdxto a new file, for examplesrc/content/pages/english/licensing.mdx. The file name becomes the URL:/licensing/. -
Change
title,description, and the Markdown body. RemovecontentPage.notice, or change it to your own note. -
Create the French and Arabic versions in
src/content/pages/french/andsrc/content/pages/arabic/with the same file name. -
Add a link to it in each
src/config/menu.<language>.jsonfile. See Menus.
For a page that reuses Margo’s sections. The archive page is the simplest model: a page header and one section.
-
Copy
src/pages/[...lang]/archive.astrotosrc/pages/[...lang]/process.astro. The file name becomes the URL:/process/. -
In the copy, change
getEntryCTM("pages", "archive", ...)togetEntryCTM("pages", "process", ...), and createsrc/content/pages/english/process.mdxwith atitle, apageHeaderblock, and the named blocks you need. Add"process"to theownRouteslist insrc/pages/[...lang]/[page].astro, so the text-page route doesn’t build it a second time. -
Replace the sections inside
<Base>with the ones you want, import each one fromsrc/layouts/components/sections/, and pass each its block:<StepsSection content={entry?.data?.stepsSection} />. -
Create the content file in the
french/andarabic/folders, and add the page to the menus. -
Run
npm run astro-check.
An AI assistant can do this for you. SKILLS.md in the theme has an “Add a new page” recipe.
Remove A Page
- Delete its route file in
src/pages/[...lang]/and its content (file or folder) insrc/content/, in every language. A text page has only the content file. - Remove its links from each
src/config/menu.<language>.jsonfile and from any content file that links to it (search the project for the URL). - Run
npm run astro-check, thennpm run build, to make sure nothing still points to it.
To hide a page without deleting it, set draft: true in its content file, in every language.
Do It With AI
Add a page at /[URL]/ made of existing Margo sections: [SECTIONS, IN ORDER]. Copysrc/pages/[...lang]/archive.astro as the route, read a new content filesrc/content/pages/english/[NAME].md, and add "[NAME]" to ownRoutes insrc/pages/[...lang]/[page].astro. Put all copy in the content file as named blocks (block namesfrom sectionsSchema in src/sections.schema.ts). Create the french and arabic content files withtranslated copy, add the page to each src/config/menu.<language>.json, then runnpm run astro-check.