Skip to content

Field Types Reference

EmDash supports 16 field types for defining content schemas. Each type maps to a SQLite column type and provides appropriate admin UI.

The following table lists every field type and its SQLite column:

TypeSQLite ColumnDescription
stringTEXTShort text input
textTEXTMulti-line text
urlTEXTURL value
numberREALDecimal number
integerINTEGERWhole number
booleanINTEGERTrue/false
datetimeTEXTDate and time
selectTEXTSingle choice from options
multiSelectJSONMultiple choices
portableTextJSONRich text content
imageTEXTImage reference
fileTEXTFile reference
referenceTEXTReference to another entry
jsonJSONArbitrary JSON data
slugTEXTURL-safe identifier
repeaterJSONRepeating group of fields

Short, single-line text. Use for titles, names, and short values.

{
slug: "title",
label: "Title",
type: "string",
required: true,
validation: {
minLength: 1,
maxLength: 200,
},
}

Validation options:

  • minLength — Minimum character count
  • maxLength — Maximum character count
  • pattern — Regular expression the value must match

Widget options:

  • None specific

Multi-line plain text. Use for descriptions, excerpts, and longer plain text.

{
slug: "excerpt",
label: "Excerpt",
type: "text",
options: {
rows: 3,
},
}

Validation options:

  • minLength — Minimum character count
  • maxLength — Maximum character count
  • pattern — Regular expression the value must match

Widget options:

  • rows — Number of rows in textarea (default: 3)

A web address. The content API rejects values that are not valid URLs.

{
slug: "website",
label: "Website",
type: "url",
required: true,
}

URL fields are stored as text. Use a string field instead when a value may be a relative path such as /about, because relative paths are not valid values for a url field.

Text intended to hold a slug-like value. This custom field type does not generate or sanitize its value.

{
slug: "legacy_slug",
label: "Legacy Slug",
type: "slug",
required: true,
unique: true,
}

Every content entry already has a reserved system slug, which EmDash manages separately for public URLs. Use a custom slug field only when the content model needs another stored slug-like value.

Decimal number. Use for prices, ratings, and measurements.

{
slug: "price",
label: "Price",
type: "number",
required: true,
validation: {
min: 0,
max: 999999.99,
},
}

Validation options:

  • min — Minimum value
  • max — Maximum value

Stored as SQLite REAL (64-bit floating point).

Whole number. Use for quantities, counts, and order values.

{
slug: "quantity",
label: "Quantity",
type: "integer",
defaultValue: 1,
validation: {
min: 0,
max: 1000,
},
}

Validation options:

  • min — Minimum value
  • max — Maximum value

Stored as SQLite INTEGER.

True or false. Use for toggles and flags.

{
slug: "featured",
label: "Featured",
type: "boolean",
defaultValue: false,
}

Stored as SQLite INTEGER (0 or 1).

Date and time value. Stored in ISO 8601 format.

{
slug: "publishedAt",
label: "Published At",
type: "datetime",
}

Storage format: 2025-01-24T12:00:00.000Z

Single selection from predefined options.

{
slug: "status",
label: "Status",
type: "select",
required: true,
defaultValue: "draft",
validation: {
options: ["draft", "published", "archived"],
},
}

Validation options:

  • options — Optional array of allowed values. Provide it to present predefined choices and reject other strings; without it, validation accepts any string.

Stored as TEXT containing the selected value.

Multiple selections from predefined options.

{
slug: "tags",
label: "Tags",
type: "multiSelect",
validation: {
options: ["news", "tutorial", "review", "opinion"],
},
}

Validation options:

  • options — Optional array of allowed values. Provide it to present predefined choices and reject other strings; without it, validation accepts any string array.

Stored as JSON array: ["news", "tutorial"]

Rich text content using Portable Text format. Supports headings, lists, links, images, and custom blocks.

{
slug: "content",
label: "Content",
type: "portableText",
required: true,
}

The value is stored as a JSON array of Portable Text blocks, for example:

[
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "Hello world" }]
}
]

Plugins can add custom block types (embeds, widgets, etc.) to the editor. These appear in the slash command menu. Rendering the saved block on the public site requires an Astro component from a native plugin or companion package. See Portable Text rendering components.

Reference to an uploaded image. Includes metadata like dimensions and alt text.

{
slug: "featuredImage",
label: "Featured Image",
type: "image",
validation: {
allowedMimeTypes: ["image/jpeg", "image/png"],
},
options: {
darkVariant: true,
},
}

Widget options:

  • darkVariant — Offer editors a second slot for an image shown in dark color schemes (default: false). See Dark Mode.

Validation options:

  • allowedMimeTypes — Non-empty list of exact MIME types accepted for the selected media

The value is stored as an object with the media reference and its metadata:

{
"id": "01HXK5MZSN...",
"src": "/_emdash/api/media/file/01HXK5MZSN...",
"alt": "Description",
"width": 1920,
"height": 1080,
"provider": "local",
"meta": {
"storageKey": "01HXK5MZSN....jpg"
}
}

With darkVariant enabled, the value can carry the dark counterpart under darkVariant, in the same shape:

{
"id": "01HXK5MZSN...",
"alt": "Architecture diagram",
"width": 1920,
"height": 1080,
"darkVariant": {
"id": "01HXK5N2QT...",
"width": 1920,
"height": 1080
}
}

Reference to an uploaded file such as a document or PDF.

{
slug: "document",
label: "Document",
type: "file",
validation: {
allowedMimeTypes: ["application/pdf"],
},
}

Validation options:

  • allowedMimeTypes — Non-empty list of exact MIME types accepted for the selected media

The value is stored as a provider reference with cached metadata:

{
"id": "01HXK5MZSN...",
"provider": "local",
"filename": "report.pdf",
"mimeType": "application/pdf",
"meta": {
"storageKey": "01HXK5MZSN....pdf"
}
}

url and size, like the other cached metadata fields, are optional. Content queries return the persisted value as-is and do not hydrate it from the media library. See File values and current metadata for the canonical lookup APIs when you need fresh metadata or a provider-specific URL.

Reference to another content entry.

{
slug: "author",
label: "Author",
type: "reference",
required: true,
options: {
collection: "authors",
},
}

Widget options:

  • collection — Target collection slug (required)

A reference is stored as the target entry ID:

"01HXK5MZSN..."

Arbitrary JSON data. Use for complex nested structures, third-party integrations, or data without a fixed schema.

{
slug: "metadata",
label: "Metadata",
type: "json",
}

Stored as-is in SQLite JSON column.

A repeating list of structured rows. Define at least one sub-field in validation.subFields; editors can then add, remove, reorder, and edit rows without entering raw JSON.

The following field stores a list of product specifications:

{
slug: "specifications",
label: "Specifications",
type: "repeater",
validation: {
minItems: 1,
maxItems: 12,
subFields: [
{ slug: "label", label: "Label", type: "string", required: true },
{ slug: "value", label: "Value", type: "text", required: true },
{ slug: "source", label: "Source", type: "url" },
],
},
}

Repeater values are stored as an array of objects:

[
{
"label": "Weight",
"value": "1.2 kg",
"source": "https://example.com/specifications"
}
]

The allowed sub-field types are string, text, url, number, integer, boolean, datetime, select, and image. Repeaters cannot contain another repeater or a complex field such as portableText, reference, or file.

Repeater validation accepts these properties:

  • subFields — One or more sub-field definitions. Each definition requires slug, label, and type; it can also set required. A select sub-field supplies its choices through options.
  • minItems — Minimum number of rows. Must be zero or greater.
  • maxItems — Maximum number of rows. Must be one or greater and cannot be less than minItems.

All fields support these common properties:

PropertyTypeDescription
slugstringUnique identifier (required)
labelstringDisplay name (required)
typeFieldTypeField type (required)
requiredbooleanRequire a value (default: false)
uniquebooleanEnforce uniqueness (default: false)
searchablebooleanInclude the field in full-text search (default: false)
indexedbooleanEnable indexed field sorting/filtering
translatablebooleanStore a value per locale (default: true)
defaultValueunknownDefault value for new entries
validationobjectType-specific validation rules
widgetstringCustom widget override
optionsobjectWidget configuration
sortOrdernumberDisplay order in admin

indexed is available for scalar fields: string, url, number, integer, boolean, datetime, select, reference, and slug. An indexed field can be passed as the orderBy field or used in fieldFilters in content list queries. Avoid indexing fields that are not used for sorting or filtering because every index adds storage and write overhead.

searchable adds the field’s text to the collection’s full-text search index. Set translatable: false for identifiers, prices, flags, and other values that must stay the same across every translation of an entry; when one locale changes a non-translatable field, EmDash synchronizes the value to its translated entries.

These slugs are reserved and cannot be used:

  • id
  • slug
  • status
  • author_id
  • primary_byline_id
  • created_at
  • updated_at
  • published_at
  • scheduled_at
  • deleted_at
  • version
  • live_revision_id
  • draft_revision_id
  • terms
  • bylines
  • byline

Import the field type definitions for programmatic use:

import type { FieldType, Field, CreateFieldInput } from "emdash";
const fieldTypes: FieldType[] = [
"string",
"text",
"url",
"number",
"integer",
"boolean",
"datetime",
"select",
"multiSelect",
"portableText",
"image",
"file",
"reference",
"json",
"slug",
"repeater",
];