Solo contract
Solo accepts a constrained subset of Astro. This page documents exactly what is and isn't supported in v1.
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.astropages 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:
stringnumberbooleannull
// ✓ 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,SEOLocalBusinessRichText
Components not on the allowlist and not yet supported trigger error E271.