Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/alineacms/alinea/llms.txt

Use this file to discover all available pages before exploring further.

Entry types describe the shape of a content entry: which fields it has, whether it can act as a parent to other entries, how it appears in the dashboard, and how its public URL is constructed. Every entry stored by Alinea belongs to exactly one type. You define types with Config.document, Config.type, or — for seeded entries — Config.page, then register them all in a schema with Config.schema.

Config.document — entries with a URL path

Config.document is the most common way to define an entry type. It automatically adds a title field, a path field (used to build the entry’s URL), and a metadata tab (SEO fields) on top of whatever custom fields you supply.
schema/BlogPost.tsx
import {Config, Field} from 'alinea'

export const BlogPost = Config.document('Blog post', {
  fields: {
    intro: Field.text('Introduction', {multiline: true}),
    body: Field.richText('Body'),
    publishedAt: Field.date('Published at', {width: 0.5})
  }
})
The generated Document type injects the following fields, organised into two tabs:
TabFieldTypeDescription
DocumenttitleTextFieldRequired entry title, displayed in the dashboard sidebar
DocumentpathPathFieldURL path segment, derived from title by default
MetadatametadataMetadataFieldSEO metadata tab (title, description, og-image)

Config.type — leaf entries without a path

Config.type creates a bare entry type without the built-in title, path, or metadata fields. Use it for structured data entries that don’t need a public URL — settings objects, global config entries, or items inside a list field.
schema/SiteSettings.ts
import {Config, Field} from 'alinea'

export const SiteSettings = Config.type('Site settings', {
  fields: {
    siteName: Field.text('Site name'),
    logo: Field.image('Logo'),
    primaryColor: Field.text('Primary color', {width: 0.5})
  }
})
Config.type is a lower-level primitive. For most content pages prefer Config.document, which gives you path-based routing out of the box.

Config.page — seeded / pre-existing pages

Config.page creates a static page descriptor that is seeded into the content tree on first run. Pass a type reference and optional fields with default values. Seeded pages cannot be deleted from the dashboard.
Seeded home page inside a root
import {Config} from 'alinea'
import {Page} from './schema/Page'

const pagesRoot = Config.root('Pages', {
  contains: [Page],
  children: {
    home: Config.page({
      type: Page,
      fields: {title: 'Home'}
    })
  }
})
You can also nest seeded pages by supplying a children map:
Config.page({
  type: Page,
  fields: {title: 'Blog'},
  children: {
    firstPost: Config.page({
      type: BlogPost,
      fields: {title: 'Hello World'}
    })
  }
})

TypeConfig options

Both Config.document and Config.type accept the same set of options via TypeConfig.
fields
FieldsDefinition
required
An object mapping field keys to field definitions created with Field.* helpers. Field keys must match [A-Za-z][A-Za-z0-9_]*. For Config.document these are merged with the built-in title, path, and metadata fields.
contains
Array<string | Type>
Types that can be created as children of entries of this type. Each item is either a Type reference or a schema key string. Types without a contains array are treated as leaf nodes.
orderChildrenBy
OrderBy | Array<OrderBy>
Default sort order for child entries shown in the dashboard sidebar.
hidden
true
When set to true, entries of this type are excluded from the dashboard sidebar content tree. Useful for internal or system-level entry types.
icon
ComponentType
A React component rendered as the entry’s icon in the sidebar and entry list. Accepts any component that renders an <svg>.
view
View
Path string to a React component that replaces the default entry editor for this type inside the dashboard (e.g. './src/views/CustomEditor.tsx#CustomEditor').
summaryRow
View
Path string to a React component rendered when this entry appears as a row in a list or search result inside the dashboard.
summaryThumb
View
Path string to a React component rendered as a thumbnail card for this entry type.
insertOrder
'first' | 'last' | 'free'
default:"'free'"
Controls where newly created child entries are inserted: at the top of the list ('first'), at the bottom ('last'), or in any position chosen by the editor ('free').
entryUrl
(meta: EntryUrlMeta) => string
A function that returns the public URL for an entry of this type. Receives an EntryUrlMeta object with status, path, parentPaths, locale, workspace, and root. Use this to implement custom URL patterns.
entryUrl({path, parentPaths}) {
  return '/' + [...parentPaths, path].join('/')
}
preview
Preview
Type-level preview configuration. Overrides any workspace- or root-level preview setting for entries of this type.

Config.schema — assembling a schema

Pass all your type definitions to Config.schema({ types: {...} }) to produce a typed Schema object. Config.schema accepts a single options argument with a required types map. The keys you use in that map become the canonical type names used in contains string references and in generated entry files.
Assembling a schema
import {Config} from 'alinea'
import {BlogPost} from './schema/BlogPost'
import {Blog} from './schema/Blog'
import {SiteSettings} from './schema/SiteSettings'

export const schema = Config.schema({
  types: {
    Blog,
    BlogPost,
    SiteSettings
  }
})
Type names must be valid identifiers ([A-Za-z][A-Za-z0-9_]*) and must not be Entry, MediaFile, or MediaLibrary — these are reserved by Alinea. Validation runs at startup and throws a descriptive error on violation.

A realistic multi-type example

The following schema models a Blog document that acts as a container for BlogPost documents. Blog uses contains to restrict which types can be nested under it.
schema/index.ts
import {Config, Field} from 'alinea'

// --- Leaf type: individual blog posts ---

export const BlogPost = Config.document('Blog post', {
  fields: {
    intro: Field.text('Introduction', {
      multiline: true,
      width: 1
    }),
    body: Field.richText('Body'),
    coverImage: Field.image('Cover image'),
    publishedAt: Field.date('Published at', {width: 0.5}),
    featured: Field.check('Feature this post', {
      description: 'Show in the featured posts section'
    })
  },
  // Custom URL: /blog/<path>
  entryUrl({path}) {
    return `/blog/${path}`
  }
})

// --- Container type: the blog index ---

export const Blog = Config.document('Blog', {
  // Only BlogPost entries can be created under a Blog entry
  contains: [BlogPost],
  fields: {
    headline: Field.text('Headline')
  }
})

// --- Schema ---

export const schema = Config.schema({
  types: {Blog, BlogPost}
})
Wire the schema and a workspace together in cms.ts:
cms.ts
import {Config} from 'alinea'
import {createCMS} from 'alinea/next'
import {schema, Blog} from './schema'

export const cms = createCMS({
  schema,
  handlerUrl: '/api/cms',
  workspaces: {
    main: Config.workspace('Main', {
      source: 'content/main',
      mediaDir: 'public',
      roots: {
        blog: Config.root('Blog', {
          contains: [Blog]
        }),
        media: Config.media()
      }
    })
  }
})
The contains array on Blog accepts a direct Type reference rather than a string. Both work — but passing the object gives you a compile-time error if BlogPost is removed from the schema, whereas a string reference is only caught at runtime validation.

Build docs developers (and LLMs) love