---
title: "Content model and media"
description: "How a site separates texts from layout, what the editor, the API and agents validate against, and how media works."
canonical: "https://atelier201.com/docs/platform/content-model"
updated: "2026-11-06"
---

# Content model and media

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](/docs/api/content/model)         | `get_content_model` | `atelier201 content model` |
| Write an entry  | [Create or update an entry](/docs/api/content/save-entry) | `save_entry`        | `atelier201 content save`  |
| Change one text | [Change one field](/docs/api/content/set-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:

```html
<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](https://www.rfc-editor.org/rfc/rfc6901)
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 `alt` text 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.
