Skip to content
New: Customize your theme with an AI assistant. Read the guide

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/.

PageURLRoute (src/pages/[...lang]/)Content (src/content/)
Solo designer home/index.astrohomepage/english/-index.md
Studio home/home-studio/home-studio.astrohomepage/english/home-studio.md
Photographer home/home-photo/home-photo.astrohomepage/english/home-photo.md
Projects/projects/projects/index.astroprojects/english/-index.md
Project or photo series/projects/<entry>/projects/[single].astroprojects/english/<entry>.md
Archive/archive/archive.astropages/english/archive.mdx
Services/services/services/index.astroservices/english/-index.md
Service page/services/<entry>/services/[single].astroservices/english/<entry>.md
Pricing/pricing/pricing.astropricing/english/-index.md
FAQ/faq/faq.astrofaq/english/-index.md
About/about/about.astroabout-us/english/-index.md
Team/team/, /team/<member>/team/team/english/
Testimonials/testimonials/testimonials.astrotestimonials/english/-index.md
Journal/blog/, posts, /blog/category/<name>/, /blog/tag/<name>/, /blog/rss.xmlblog/blog/english/, authors in author/english/
Contact/contact/contact.astrocontact/english/-index.md
Legal and text pages/privacy-policy/, /terms-conditions/, /cookie-policy/[page].astropages/english/*.md
Shortcode demo/components/[page].astropages/english/components.mdx
Layout gallery/elements/elements.astropages/english/elements.mdx
404any missing URL404.astrosections/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.
src/content/pages/english/archive.mdx
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.

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.

src/content/services/english/-index.md
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.

FieldWhat it does
title, descriptionThe page title and summary
metaTitle, metaDescriptionReplace the title and description in search results only
imageThe cover or social preview image of the page
date, updatedPublish date and last edit. Text pages show “Last updated”
drafttrue leaves the page out of the built site. It still shows in npm run dev
customSlugA different URL segment than the file name. Use the same value in every language
canonical, robots, keywordsSEO overrides
excludeFromSitemap, excludeFromSearch, excludeFromAiLeave the page out of the sitemap, site search, or the AI files
disableTaglineDon’t add the site tagline to this page’s title
headerLayoutAnother header layout on this page: pill, bar, or overlay
footerLayoutAnother footer layout on this page: wordmark, columns, or email
contactDocktrue 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.

src/content/pages/english/privacy-policy.mdx
title: "Privacy policy"
description: "What I collect when you write to me..." # the line under the title
date: 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 text

The 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.

  1. Copy src/content/pages/english/terms-conditions.mdx to a new file, for example src/content/pages/english/licensing.mdx. The file name becomes the URL: /licensing/.

  2. Change title, description, and the Markdown body. Remove contentPage.notice, or change it to your own note.

  3. Create the French and Arabic versions in src/content/pages/french/ and src/content/pages/arabic/ with the same file name.

  4. Add a link to it in each src/config/menu.<language>.json file. See Menus.

Remove A Page

  1. Delete its route file in src/pages/[...lang]/ and its content (file or folder) in src/content/, in every language. A text page has only the content file.
  2. Remove its links from each src/config/menu.<language>.json file and from any content file that links to it (search the project for the URL).
  3. Run npm run astro-check, then npm 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]. Copy
src/pages/[...lang]/archive.astro as the route, read a new content file
src/content/pages/english/[NAME].md, and add "[NAME]" to ownRoutes in
src/pages/[...lang]/[page].astro. Put all copy in the content file as named blocks (block names
from sectionsSchema in src/sections.schema.ts). Create the french and arabic content files with
translated copy, add the page to each src/config/menu.<language>.json, then run
npm run astro-check.
Type something to search...