Fields
Fields make data editable. Every field is created with a Field function that takes a label and an options object, and is added to the fields of a Type under the name its value is stored as. Pick a field below for its options and the value it stores, or try them all in the playground further down. If none fits, you can write a custom field.
Basic
Field.textField.richTextField.selectField.numberField.checkField.dateField.timeField.codeField.path
Links and media
Field.linkField.entryField.urlField.imageField.file
Structure
Field.listField.objectField.tabs- LocaliserNew in 2.0
Field.localiser
Also available: Field.metadata, Field.json and Field.view. Need something else? Build a custom field with the dashboard components.
Try them
Every field type in one form, edit the code to see the dashboard change.
Common options
Most fields accept these options next to their own. The field pages list the exceptions.
help: instructions shown below the label. Plain text or a React node.width: a number between 0 and 1 to make the field take part of a row,0.5puts two fields side by side.inline: a compact version of the field: the label is hidden and used as placeholder where the input allows it.initialValue: the value of the field when an entry or list row is created.required: the field must have a value. Empty strings,null, empty lists, empty links and rich text without text count as missing.validate: a function that receives the value and returns an error message to show,falsefor a generic "Field is invalid", ortrue/undefinedwhen the value is fine.readOnly: show the value but don't allow editing.hidden: don't show the field in the dashboard. Its value is kept and can still be queried or set with a conditional option.shared: in a translated root, keep the same value in every locale, see below.overview(deprecated):trueshows the field as a column in the list of the parent's children. It's only used when the parent'soverviewdefines nocolumns, for up to five fields. Defineoverview.columnson the parent instead.
import {Field} from 'alinea'
Field.text('Discount code', {
help: 'Uppercase letters and digits only',
width: 0.5,
required: true,
validate(value) {
if (!/^[A-Z0-9]*$/.test(value)) return 'Use uppercase letters and digits'
}
})Required fields and validation messages are shown on the field while an editor works on the entry. An entry can't be published while a field fails these checks: the dashboard lists the invalid fields, and publishing through the API or the MCP server is rejected with the field paths and messages. Drafts can be saved with errors, and hidden or read-only fields are not checked.
Shared fields
In a root with i18n, set shared: true on fields that should be the same in every language, such as a price, a date or a product image. When the entry is published, its shared values are copied to the other translations, and new translations start with them.
import {Config, Field} from 'alinea'
export const Product = Config.document('Product', {
fields: {
// Translated per locale
description: Field.richText('Description'),
// The same in every locale
price: Field.number('Price', {shared: true}),
image: Field.image('Image', {shared: true})
}
})shared only works on fields at the top level of a type, not on fields inside an object, list or rich text block. It isn't supported on path fields. To translate a single value while sharing the rest of an entry, use Field.localiser instead.
Conditional options
Options can depend on the values of other fields. Wrap a field in Config.track.options with a function that reads values through get and returns the options to change. It runs again whenever a value it reads changes, and has to return synchronously.
import {Config, Field} from 'alinea'
const linkType = Field.select('Link to', {
initialValue: 'page',
options: {page: 'A page', external: 'An external url'}
})
export const CallToAction = Config.type('Call to action', {
fields: {
label: Field.text('Label'),
linkType,
page: Config.track.options(Field.entry('Page'), get => ({
hidden: get(linkType) !== 'page'
})),
url: Config.track.options(Field.url('Url'), get => ({
hidden: get(linkType) !== 'external'
}))
}
})get reads fields of the entry being edited, including top-level fields from inside a list row. Hidden fields keep their value, so check the controlling field when you render the entry too.
Layout
Fields appear in the order you define them. Besides width and tabs, Field.view places a React element between fields, such as a divider or a note for editors. Spread it into fields like tabs:
import {Config, Field} from 'alinea'
export const Settings = Config.type('Settings', {
fields: {
title: Field.text('Site name'),
...Field.view(<hr />),
analyticsId: Field.text('Analytics id', {help: 'Leave empty to disable'})
}
})To hide or lock fields for some editors, set field permissions on a role, see Roles and permissions.