Frontmatter

Solo contract

Solo accepts a constrained subset of Astro. This page documents exactly what is and isn't supported in v1.

Check before you build. Run npx @withfrontmatter/solo-check to validate your project against this contract automatically.

Pages

Pages in src/pages/** must follow explicit composition rules.

Accepted:

  • Explicit imports
  • Component composition with literal props
  • Exactly one layout per page
  • Deterministic components — same input, same output
  • Static HTML

Rejected:

  • Business logic in page frontmatter
  • Dynamic layout selection
  • Runtime Astro features (client:*, islands)
  • Arbitrary JS computation in frontmatter (sort, filter, Object.values, new Date)
  • Markdown pages (.md) — use .astro pages only

Layouts

Layouts in src/layouts/**.

Accepted:

  • Multiple layouts in the project
  • Exactly one layout per page
  • Static, explicit layout composition

Rejected:

  • Nested layouts (a layout that composes another layout)
  • Dynamic layout selection

Props

Solo v1 accepts literal props only.

Accepted types:

  • string
  • number
  • boolean
  • null
// ✓ Accepted
<Hero headline="Hello Solo" />
<CTA title="Contact" buttonHref="/contact/" />
<Card price={89.99} featured={true} />
// ✕ Rejected — error E201
<Hero headline={page.title} />
<CTA title={site.cta.title} />
<Hero headline={"Hello " + name} />

CSS

Accepted:

  • Global CSS files
  • Global SCSS compiled by Astro (import "../styles/styles.scss" in the layout)
  • Inline style="" attributes
  • <link rel="stylesheet"> tags

Rejected — error E230:

  • Component <style> blocks (scoped CSS)
  • CSS modules
  • Astro's local component styling pipeline

Images

Accepted:

  • Static images in public/images/ — referenced as /images/photo.jpg
  • Absolute external URLs

Out of scope:

  • Astro's image optimization pipeline (<Image />, <Picture />)
  • Responsive image generation
  • Complex asset manifests

Framework components

React, Vue, and Svelte components present in the project are skipped — not compiled, not rejected as errors.

They generate a placeholder in the output:

<div
  data-fm-skipped="react"
  data-fm-component="FancyWidget"
  data-fm-source="src/components/FancyWidget.tsx"
  data-fm-props='{"productId":"abc"}'
></div>

And an entry in manifest.json. In --strict mode, framework components become blocking errors.

Blessed components

The following components are explicitly supported via an allowlist — not via a generic engine:

  • SEOPage, SEOWebsite, SEOArticle, SEOOrganisation, SEOLocalBusiness
  • RichText

Components not on the allowlist and not yet supported trigger error E271.

On this page