Modelling content
Modelling content
What schema is, the three layers it is read in, where each file goes, and the commands that write and check it.
Schema is where you say what content exists. This guide covers the ideas every other modelling guide builds on.
After reading this guide, you will know:
- What schema describes, and why it is files rather than settings in a database.
- How the three layers of schema combine, and how to override one file.
- Where each kind of schema file goes.
- How to generate schema files and check them.
1. Content has a shape, and the shape is a file
Tidewater’s blog posts each have a title, a body, an author, some topics and a publish date. That list — the shape of a post — is schema. The posts themselves are content.
Nibble keeps the two apart on purpose:
| Where it lives | Who changes it | How it ships | |
|---|---|---|---|
| Schema — collections, blueprints, forms… | YAML files in the repository | a developer | in a commit, reviewed, deployed |
| Content — the posts, pages and terms | rows in SQLite | editors, in the control panel | the moment they publish |
Because the shape is a file, a new field is reviewed in a pull request, arrives on every environment at once, and can be rolled back like any other change. Nobody clicks a field into existence on production and forgets to do the same on staging.
Note
There is one exception to “content is rows”: a collection can be written as files, like
Tidewater’s help centre. Its shape is still schema; its content is Markdown in site/content/.
2. The kinds of schema
| Kind | What it declares | Tidewater’s |
|---|---|---|
| Collection | a kind of content, and its URLs | pages, posts, help |
| Blueprint | the fields one kind of entry or term has | page, post, article |
| Fieldset | a group of fields several blueprints import | Nibble’s seo |
| Taxonomy | terms entries relate to | authors, topics |
| Global | one set of site-wide fields | site, seo, integrations |
| Navigation | a menu | main, footer, help |
| Form | a public form and where submissions go | demo |
3. Three layers
Schema is read from three places, in order, and the last to define a handle wins:
- Nibble’s own —
vendor/nibble/core_schema/: pages, posts, the SEO fieldset, the site, SEO and integrations globals, and the main and footer menus. - The active theme’s —
site/themes/<theme>/schema/: whatever the theme needs to render, such as theauthorsandtopicstaxonomies and richer page and post blueprints. - The site’s own —
site/schema/: anything specific to this site.
Tidewater’s help collection exists only in site/schema/. Its posts collection is declared by Nibble and again in
site/schema/, and the one in site/schema/ is the one that counts. The post blueprint comes from the theme, which
overrides Nibble’s.
Important
An override replaces the whole file. It does not merge into the one it overrides. Tidewater’s
site/schema/collections/posts.yml repeats the route and template because, without them, the collection would have
none.
To see the schema as Nibble sees it after all three layers:
bin/rails nibble:schema:show
Tip
Put schema a theme needs to render in the theme, and schema that is about this site’s content in site/schema/.
Keep the pricing block in site/themes/tidewater/schema/, because the theme draws it, and the help centre
in site/schema/, because it is Tidewater’s content, not the theme’s idea.
4. Where the files go
site/schema/
├── collections/help.yml
├── blueprints/
│ ├── collections/help/article.yml
│ └── taxonomies/topics/topic.yml
├── taxonomies/topics.yml
├── fieldsets/cta.yml
├── globals/company.yml
├── navigation/help.yml
├── forms/demo.yml
└── migrations/2026_10_01_rename_intro.yml
A blueprint lives under the collection or taxonomy it belongs to: blueprints/collections/<collection>/<blueprint>.yml.
Content migrations are covered in Changing the schema.
5. Generating schema
Generators write a valid stub in the right place, so you never start from a blank file or a copy of Nibble’s:
bin/rails nibble:generate:collection case_studies
bin/rails nibble:generate:taxonomy industries
bin/rails nibble:generate:blueprint case_studies/video
bin/rails nibble:generate:fieldset cta
bin/rails nibble:generate:global company
bin/rails nibble:generate:navigation help
bin/rails nibble:generate:form demo
nibble:generate:collection case_studies, for example, writes a dated collection at /case-studies/{slug} and a
blueprint with a single body field. Edit them from there.
Warning
Do not copy a file out of vendor/nibble/core_schema/ to start one of your own unless you mean to override it. A
copied file with the same handle is an override, and it stops following Nibble’s version from then on.
6. Checking schema
bin/rails nibble:check
nibble:check reads every layer and reports every problem at once: a field type that does not exist, a
collection pointing at a template the theme does not have, a form with a bad address, generated TypeScript types
that are out of date. It also reports content the schema no longer covers — a field removed while entries still
hold values for it.
Tip
When nibble:check says the types are out of date, run bin/rails nibble:schema:types. The generators refresh
them for you; changing a view’s sidecar or a blueprint by hand does not.
It runs before every deploy, and a deploy it refuses never goes live. Run it whenever you change anything under
site/schema/.
7. What’s next
- Collections — declaring Tidewater’s blog and help centre.
- Blueprints and fields — the landing page’s blocks, and every fieldtype.