Skip to content

Type

A type describes one kind of entry: the fields editors fill in and how entries of that type behave in the dashboard. Create one with Config.type(label, options) and add it to the schema to make it available as an entry. For pages you'll usually reach for Config.document, which is a type with title, path and metadata fields included.

import {Config, Field} from 'alinea'

export const Author = Config.type('Author', {
  fields: {
    title: Field.text('Name', {required: true, width: 0.5}),
    path: Field.path('Path', {required: true, width: 0.5}),
    bio: Field.text('Bio', {multiline: true})
  }
})

The label ("Author") is what editors see. The name the type is stored under is its key in the schema.

Options

  • fields (required): the fields of the type, keyed by name. Field names must start with a letter and contain only letters, digits and underscores. Spread tabs or Field.view(...) sections in between to lay out the form.

  • contains: the types that can be created as children of this entry, by schema name or by reference. Without it, entries of this type can't have children.

  • overview: how the dashboard lists the children: columns, the default order (sort), the layout and actions. See Overviews.

  • insertOrder: where new children are added: 'first', 'last' or 'free' (default), which lets the editor choose in the create dialog.

  • entryUrl: a function that computes the url of entries of this type, see below.

  • icon: a React component shown next to entries of this type in the sidebar. Icon sets from Icones can be copied as components.

  • hidden: set to true to leave entries of this type out of the sidebar tree. They can still be linked to and queried.

  • defaultView: 'edit' opens the form when an entry is selected, 'overview' shows a table of its children first. The default is 'overview' for entries that have children and 'edit' otherwise.

  • preview: turn live previews on or off for this type (true/false), or pass a React component that renders the preview. It overrides the setting of the root, workspace and config.

  • view: a React component, or a path to one, that replaces the entry editor for this type. It receives {type}.

  • orderChildrenBy (deprecated): sort children by one or more fields. Use overview.sort, which still accepts the same value.

  • summaryRow, summaryThumb (deprecated): not used by the dashboard. Configure the columns and card image in the overview of the parent instead.

Entries need a title

Every entry has a title: it's shown in the sidebar, used for search and returned as Query.title. A type that editors create entries of needs a title text field. Add a path field to let editors control the url segment, otherwise it's derived from the title. Config.document includes both.

Types that are only used as blocks in a List or Rich text field don't need either.

Children

contains turns a type into a container. Here a Blog holds posts, and new posts are added at the top of the list:

import {Config, Field} from 'alinea'

export const BlogPost = Config.document('Blog post', {
  fields: {
    publishDate: Field.date('Publish date')
  }
})

export const Blog = Config.document('Blog', {
  contains: ['BlogPost'],
  insertOrder: 'first',
  fields: {}
})

Use overview.sort when children should always be sorted by a field instead of by hand. The overview also sets the columns of the list of children:

import {Config, Field, Query} from 'alinea'

export const Event = Config.document('Event', {
  fields: {date: Field.date('Date')}
})

export const Agenda = Config.document('Agenda', {
  contains: ['Event'],
  fields: {},
  overview: {
    sort: [{desc: Event.date}, {asc: Query.title}],
    columns: {
      date: Config.column({header: 'Date', width: 140, select: Event.date})
    }
  }
})

The sort and insertOrder only affect the dashboard. Queries return children in their stored order unless you pass an orderBy. See Overviews for the other options.

Urls

An entry's url is built from the paths of its parents and its own path, prefixed with the locale in translated roots: /blog/hello-world or /fr/blog/hello-world. A path of index is left out, so an index entry at the top of a root gets /.

entryUrl replaces that url for a type. It receives the default url and the data it's built from: path, parentPaths, locale, workspace, root, status, data (the entry's field values) and defaultUrl.

import {Config} from 'alinea'

export const NewsArticle = Config.document('News article', {
  // Always /news/<path>, wherever the article sits in the tree
  entryUrl({path, locale}) {
    return locale ? `/${locale}/news/${path}` : `/news/${path}`
  },
  fields: {}
})

Urls are computed when content is saved and indexed, and stored with the entry. That's what lets you query by url (cms.get({url})) in a catch-all route. Keep them in line with your routes: a page that isn't served on its url shows up broken in previews and links.

Two entries in the same root can't be published on the same url: publishing the second one fails with a message naming the entry that already uses it. Keep that in mind when entryUrl drops the parent paths.

Good to know

Fields must be unique

A field instance can be used only once: Alinea throws "is already in use" when the same field shows up twice in a type or in two types of your schema. Queries rely on this to know which type and name a field reference like BlogPost.publishDate points to. Create shared fields with a function so every use gets its own instance:

import {Config, Field} from 'alinea'

// Not allowed: the same instance in two places
// const summary = Field.text('Summary')

// Fine: every call creates a new field
const summary = () => Field.text('Summary', {multiline: true})

export const Article = Config.document('Article', {
  fields: {summary: summary()}
})

export const Event = Config.document('Event', {
  fields: {summary: summary()}
})

Type references are fields

A type exposes its fields as properties: BlogPost.publishDate is the field itself. Use these references to select, filter and sort in queries, in overviews and in conditional options. The TypeScript type of an entry is available with Infer.