Skip to content

Building a theme

Blueprints and fields

The form an editor fills in, and every fieldtype you can put on it.

A blueprint is two things at once: the form an editor fills in, and the shape the stored data is checked against. Writing one is the closest thing in Nibble to designing a page.

# schema/blueprints/collections/guides/guide.yml
schema: 1
title: Guide
tabs:
  main:
    display: Content
    sections:
      - fields:
          - handle: body
            field:
              type: rich_text
              display: Body
              localizable: true
          - handle: region
            field:
              type: terms
              taxonomies: [regions]
              max_items: 1
  seo:
    display: SEO
    sections:
      - fields:
          - import: nibble::seo

Tabs group the form. Sections group fields within a tab, and can carry an instruction line. Fields each have a handle — the name the data is stored under, which your views use — and a field describing the type and its options.

Common options on any field: display (its label), instructions (help text under it), required, width (percentage of the row), localizable, listable (offered as a column in the listing), and read_only.

The fieldtypes

Group Types
Text text, textarea, rich_text, markdown, slug, secret
Numbers and dates integer, date
Choice select, radio, checkboxes, toggle, list
Relationships entries, terms, relationship, link
Files assets, files
Repeating grid, replicator
Whole-page seo

A few worth knowing properly:

rich_text is the full editor: headings, lists, links, images, tables, code blocks with a language picker, find and replace. It is stored as structured data, not HTML, and rendered to HTML on the server — see Theme helpers.

markdown is a plain textarea with a Preview button. The preview is rendered by the server, so it shows exactly what the theme will receive. Turn on sanitize when the text might come from someone you do not trust.

replicator lets an editor build a page from blocks in any order. Each set you define renders through themes/<yours>/views/sets/<name>.vue, which is how a flexible page is assembled without giving anyone a blank HTML box.

grid is a simple repeating row of fields — opening hours, a price table.

entries and terms open a picker rather than a text box, and store a reference. If the target moves or is renamed, the reference still points at it.

Fieldsets: writing a group once

A fieldset is a group of fields imported into several blueprints:

- import: nibble::seo

Nibble ships nibble::seo, nibble::page_fields, nibble::post_fields and nibble::featured_image. Make your own with bin/rails nibble:generate:fieldset <name>, and import it as <name>.

Change the fieldset and every blueprint using it changes — which is the point, and the reason to reach for one whenever the same three fields appear twice.

Conditional fields

A field can appear only when another has a particular value:

- handle: video_url
  field:
    type: text
    if: { layout: video }

The form hides and shows it as the editor works, and validation follows — a hidden field is not required. The available keys are if, if_any, unless, unless_any, show_when, show_when_any, hide_when and hide_when_any; the _any forms pass when any one condition matches rather than all of them.

Generated types

Every blueprint contributes to themes/<yours>/.nibble/types.d.ts, so your views have real TypeScript types for the fields they render, and an editor that autocompletes them.

It is regenerated by the generators and by bin/rails nibble:schema:types, and bin/rails nibble:check fails if it is stale — so a blueprint change that was never reflected in the types cannot reach production.

Previous
Schema