Skip to content

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.textstring
  • Field.richTextTextDoc
  • Field.selectoption key
  • Field.numbernumber
  • Field.checkboolean
  • Field.datedate string
  • Field.timetime string
  • Field.codestring
  • Field.pathURL segment
  • Field.linkentry, URL or file
  • Field.entryentry reference
  • Field.urlexternal link
  • Field.imageimage reference
  • Field.filefile reference

Structure

  • Field.listarray of rows
  • Field.objectnested object
  • Field.tabslayout only
  • LocaliserNew in 2.0
    Field.localiservalue per locale

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.5 puts 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, false for a generic "Field is invalid", or true/undefined when 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): true shows the field as a column in the list of the parent's children. It's only used when the parent's overview defines no columns, for up to five fields. Define overview.columns on 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.