Schemas
A schema describes the fields an object of one type has and what values they accept.
Schemas are stored in /schemas/, one .json or .yml file each. The file name is the type: /schemas/Hero.json defines Hero.
// /schemas/Hero.json
{
"fields": {
"heading": {
"type": "string",
"default": "Join our mission."
},
"cta": {
"type": "fragments",
"default": [
{
"_type": "Button",
"label": "Join Now"
}
],
"allow": ["Button"]
}
}
}
Anatomy
A schema is an object. Only fields is required, all other properties are optional.
| property | description | value |
|---|---|---|
| fields | The fields an object of this type has (see Fields) | object |
| label | Readable name of schema | string |
| description | Short explanation of what this schema is used for | string |
| document | Whether objects are stored as their own file, or nested as fragments | boolean |
| singleton | Whether only one document of this type can exist, like site settings | boolean |
| path | Folder in /documents/ where new documents of this type are created | string |
| key | Field that uniquely identifies each document (see Fields) | string |
| pull, push | Sync documents with an external API (see Pull & push) | object |
Fields
Each field is defined by its properties.
| property | description | value |
|---|---|---|
| type | Type of the field | string |
| label | Human-readable field title | string |
| description | Tooltip to describes field's usage | string |
| placeholder | Placeholder text on field | string |
| nullable | Whether or not field value can be null | boolean |
| hidden | Whether or not field should be shown | boolean |
| immutable | Whether or not field can be edited | boolean |
| min | Minimum value (or length) | number |
| max | Maximum value (or length) | number |
| step | Increment and decrement size | number |
| options | Available options to choose from | {value,label?}[] |
| allow | Allowed fragment/document types | string[] |
| default | Default value (optional, falls back per type) | any |
Below is a list of all available field types that you can use in schemas.
| type | description | value |
|---|---|---|
| string | Single-line text | string |
| number | Numeric value | number |
| boolean | True/false toggle | boolean |
| date | ISO 8601 date string | string |
| datetime | ISO 8601 datetime string | string |
| color | Hex color picker | string |
| url | URL string | string |
| slug | Unique slug | string |
| option | Dropdown from fixed choices | string |
| richtext | Structured rich text content blocks | object[] |
| text | Single-line text blocks | object[] |
| image | Image with src and alt | object |
| reference | Reference to another document | object |
| references | References to other documents | object[] |
| fragment | Single nested fragment | object |
| fragments | Ordered list of nested fragments | object[] |
A text value is a list of runs, one per change of marks, each { _type: "text", text } with its own bold, italic or underline: [{ _type: "text", text: "Join our " }, { _type: "text", text: "mission", bold: true }].
A reference or references field that isn't nullable will be completed automatically when set to null: a reference gets the first document matching its allow types, references gets all of them (up to max). In some cases, this is prefered over hardcoding a reference.
key names a field that identifies a document (or submission) of the schema: no two of the same type may share a value, and once set it can't be changed in the editor. The field must be a nullable, immutable string, number, email or url (with default set to null), meaning no key yet.
Pull & push
A schema can exchange documents with an external API. Both use the same keys, in opposite directions:
pull: API to documents. Each item becomes a document, matched on the schema'skey, so pulled documents can be renamed freely. Only mapped fields are overwritten.push: documents to API. Each document (or submission) is sent as a request. Nothing is written back.
# /schemas/Product.yml
document: true
path: /products
key: slug
fields:
slug: { type: string, nullable: true, immutable: true, default: null }
title: { type: string, default: "" }
price: { type: number, default: 0 }
pull:
context: Settings
request:
url: https://api.webflow.com/v2/collections/{{collection}}/items?offset={{offset}}
headers:
Authorization: Bearer {{token}}
init:
offset: "0"
next:
offset: offset + 100
select: response.body.items
map:
slug: fieldData.slug
title: fieldData.name
price: +fieldData.price
# /schemas/Newsletter.yml
document: true
key: email
fields:
email: { type: email, default: "" }
push:
context: Settings
request:
url: https://us1.api.mailchimp.com/3.0/lists/{{list}}/members
headers:
Authorization: Bearer {{token}}
map:
email_address: email
status: "'subscribed'"
| property | description | value |
|---|---|---|
| request | url, method, headers, body, with {{expr}} placeholders | object |
| context | A singleton schema type whose document provides values | string |
| init | Values for the first request | object |
| next | Values for the next request. Stops when one is null | object |
| select | Path to start from, map reads from here | string |
| map | Map response (pull) or request (push) properties onto document properties | object |
| prune | Delete documents the API no longer returns (pull only, default true) | boolean |
A {{expr}} placeholder can read the context document's fields, merged with init and then next. Every value is an expression, the same safe subset of JavaScript as in components (field access, comparisons, &&/||/??, ternaries, method calls like .split()), so text needs quotes: "'abc'".
A nested list mapped onto a fragments field doesn't need _type on its items when allow names one type: it is filled in, and each item's fields are coerced into their types, at any depth.
An array in select gives one document per item (pull) or one request per item (push). Both run on a whole schema type, or on one document.