Astro
Making component pages editable
Many Astro themes build their main pages — home, services, pricing, about — from
components, with the words written straight into the .astro file:
<Hero
tagline="Free & open source"
title="Websites that load fast"
actions={[{ text: 'Get started', href: '/start', variant: 'primary' }]}
/>
Orbit edits content, not code, so those words aren’t editable as they stand.
Orbit won’t rewrite your .astro files for you — every theme is different, and
changing a page’s code is a developer’s call. Instead, move the words into a content
entry and let the page read them. The design stays in code; the words become a form.
That’s the whole of making a component page Orbitable.
1. Describe the page’s words in your content config
Add a collection for the page, with one field per piece of text. Keep the fields
in content.config.ts itself — Orbit reads that file but doesn’t follow imports
from your own modules, so a schema imported from elsewhere would show no fields.
// src/content.config.ts
const text = z.string().optional();
const action = z.object({
text: z.string(),
href: z.string(),
variant: z.enum(['primary', 'secondary']).optional(),
});
const homePage = defineCollection({
loader: glob({ pattern: 'home.md', base: 'src/content/pages' }),
schema: z.object({
hero: z.object({ tagline: text, title: text, actions: z.array(action).optional() }).optional(),
features: z
.object({
title: text,
items: z.array(z.object({ title: text, description: text })).optional(),
})
.optional(),
}),
});
export const collections = { /* …your other collections… */ home: homePage };
A pattern naming a single file (home.md) makes it a fixed page: editors can
change it but not add more.
2. Put the words in the entry
---
# src/content/pages/home.md
hero:
tagline: Free & open source
title: Websites that load fast
actions:
- text: Get started
href: /start
variant: primary
features:
title: What you get
items:
- title: Fast
description: Pages that load in a blink.
---
3. Have the page read the entry
---
import { getEntry } from 'astro:content';
import Hero from '~/components/widgets/Hero.astro';
import Features from '~/components/widgets/Features.astro';
const page = (await getEntry('home', 'home'))!.data;
---
<Layout>
{page.hero && <Hero {...page.hero} />}
{page.features && <Features id="features" {...page.features} />}
</Layout>
The components are unchanged: they get the same props as before, just from the entry instead of the code.
4. Tell Orbit it’s a form-only page
# orbit.config.yaml
collections:
home:
canAdd: false # one fixed page
bodyless: true # no text editor — every word is a field
What to keep in code
Anything that’s design rather than words stays in the .astro page:
- layout and order of sections, and which ones appear;
- CSS classes, colours, backgrounds, spacing and animation settings;
- decorative extras (a divider, a trailing icon).
If a title mixes words and styling — say one highlighted phrase — split it into two
fields (title and titleAccent) and add the styling in the page, so editors never
see class names.
Text fields can still take simple HTML (<strong>, <code>, <br>) if your
components render them as HTML.
A worked example
The AstroWind business site in Orbit Extras
is converted this way: the words of its home, about, services, pricing and contact
pages — heroes, features, team, testimonials, prices, FAQs and the rest — are forms in
Orbit, while its design is untouched. A small helper reads each page’s entry, and a
shared page() function in content.config.ts keeps the five schemas short.