Content model and media
How a site separates texts from layout, what the editor, the API and agents validate against, and how media works.
Every site is an Astro project that keeps what the owner changes in typed content files, and keeps layout in code. A page reads the content and lays it out. It never contains the business name, a price or opening hours itself.
The content model is the list of those content types and their fields. It
is defined once in the project’s src/content.config.ts (a Zod schema per
type). Each build exports it as JSON Schemas, and the editor, the REST API, MCP
and the CLI validate every change against it. A value that does not fit is
refused with the field name, and nothing is written.
The model comes from the latest build. Build a new site once before you read its content model.
Collections and singletons
A collection holds many entries, one file each. The file name is the entry’s slug: lowercase letters, digits and hyphens. A singleton is one JSON document at a fixed path.
The Atelier201 starter ships these:
| Name | Kind | What it holds |
|---|---|---|
business | singleton | Name, tagline, description, phone, email, address, opening hours, socials, legal details, booking and map links, and the site’s language |
theme | singleton | Four brand colours, corner radius, font pair and light, dark or auto mode |
navigation | singleton | Header links, footer links and the call-to-action button |
integrations | singleton | Google Analytics id, Plausible domain, and code added to the head or the end of the body |
services | collection, JSON | Services with a name, description, price, order and image |
blog | collection, Markdown | Posts with a title, description, date, author, cover image, tags, the services they are about (a relation to services, by slug) and a draft flag |
The starter is a skeleton: it ships these two collections as examples and carries no opinion about the business. A site deletes the ones it does not need and adds its own (a team, a menu, events).
A content collection is Markdown: fields in the front matter and a body. A data collection is JSON with no body.
A new entry created through the API or MCP starts as a draft when its collection has a draft field, so a person decides to show it.
Change content without breaking the layout
Prefer the content calls to raw file edits for texts, entries and business details:
| Task | REST | MCP | CLI |
|---|---|---|---|
| Read the model | Read the content model | get_content_model | atelier201 content model |
| Write an entry | Create or update an entry | save_entry | atelier201 content save |
| Change one text | Change one field | set_field | atelier201 content set |
Use the file calls only to change code: a layout, a component, or the content
model itself. To add a field, add it in src/content.config.ts with
.default() or .optional() so existing entries stay valid.
Inline editing: data-atelier201-field
The owner clicks a short text on the page and edits it in place. That works because the text carries the address of the field it comes from:
<h1 data-atelier201-field="business:/name">Morning Crumb Bakery</h1>
<time data-atelier201-field="business:/hours/2/close">15:00</time>The address is <singleton>:<JSON pointer>: business, theme or
navigation, a colon, then an RFC 6901
pointer into that singleton’s file. The same address works with set_field and
atelier201 content set.
Keep these rules when you edit a component:
- Put the attribute on an element whose whole text is exactly that field, with
nothing else inside. Atelier201 replaces the element’s text. For “Call us: 555 0100”,
wrap only the number in a
span. - Move the attribute with the element when you restyle it, and add it wherever you show a singleton field in a new place.
- Do not put it on text built from several fields, transformed text (a formatted
date), Markdown bodies or collection entries. An entry on a page keeps
data-atelier201-entry="<collection>/<slug>"on its container instead.
The site stays static
A site built for Atelier201 is static Astro: no server adapter, no server-side
rendering, no client-side framework, and no request to another origin at
runtime (fonts, analytics tags and embedded maps included). It writes one
language, set in business.locale. Its dependencies are astro and @astrojs/mdx
(off until mdx() is added to the config), because the build runs without network access. The starter’s AGENTS.md lists these rules
for an agent editing the code.
Media
Upload an image, a PDF or a short video to the site, then reference it as
/media/<filename> in content, for example in a blog post’s coverImage.
- Each file can be 20 MB. Through MCP, uploads are base64-encoded and limited to 5 MB.
- Give an image an
alttext for people who cannot see it. - Media counts against the plan’s storage: 100 MB on a draft and 5 GB on a Site.
- An upload is included from the next build. Deleting a file removes it from later builds, and builds already published keep their copy until a newer publication replaces them.