You're reading docs for Nextly Alpha. APIs may change between releases.

Configuration

Fields

Complete reference for every field type, their options, and usage patterns.

Fields define the shape of your content. Every collection, single (formerly global), and component is composed of fields.

Nextly provides field helper functions exported from @nextlyhq/nextly/config. These eliminate the need to manually set the type property and give you full TypeScript autocomplete.

import { text, number, select, option } from '@nextlyhq/nextly/config';

const fields = [
  text({ name: 'title', required: true }),
  number({ name: 'price', min: 0 }),
  select({
    name: 'status',
    options: [option('Draft'), option('Published')],
  }),
];

Common Field Options

These options are available on every field type:

OptionTypeDefaultDescription
namestring--Required. Unique field identifier. Lowercase, no spaces.
labelstringAuto from nameDisplay label in the Admin UI.
requiredbooleanfalseWhether the field must have a value.
uniquebooleanfalseEnforce uniqueness at the database level.
indexbooleanfalseCreate a database index for faster queries.
defaultValueany | function--Static value or (data) => value function.
validatefunction--Custom validation. Return true or an error message string.
localizedbooleanfalseStore separate values per locale. (Reserved for future use.)
customRecord<string, unknown>--Arbitrary metadata for plugins or hooks.

admin Options

Control how the field appears in the Admin UI:

OptionTypeDefaultDescription
admin.position'sidebar'--Place the field in the sidebar instead of the main area.
admin.width'25%' | '33%' | '50%' | '66%' | '75%' | '100%''100%'Field width in the form layout.
admin.descriptionstring--Help text below the field label.
admin.placeholderstring--Placeholder text when empty.
admin.readOnlybooleanfalseMake the field read-only in the UI.
admin.hiddenbooleanfalseHide the field from the UI entirely.
admin.disabledbooleanfalseDisable the input.
admin.classNamestring--Custom CSS class on the field wrapper.
admin.styleobject--Inline styles on the field wrapper.
admin.conditionFieldCondition--Conditionally show/hide the field. See below.
admin.componentsobject--Custom React components for Field, Cell, or Filter rendering.

Conditional Logic

Show or hide a field based on another field's value:

text({
  name: 'externalUrl',
  admin: {
    condition: {
      field: 'linkType',
      equals: 'external',
    },
  },
})

Available condition operators: equals, notEquals, contains, exists.

access Options

Field-level access control with per-operation granularity:

text({
  name: 'internalNotes',
  access: {
    create: ({ req }) => req.user?.role === 'admin',
    read: ({ req }) => req.user?.role === 'admin',
    update: ({ req }) => req.user?.role === 'admin',
  },
})

hooks Options

Field-level lifecycle hooks:

HookWhen it runs
beforeValidateBefore validation. Can transform the value.
beforeChangeBefore saving to the database.
afterChangeAfter saving to the database.
afterReadAfter reading from the database. Can transform the returned value.
text({
  name: 'slug',
  hooks: {
    beforeValidate: [
      async ({ value, data }) => {
        if (!value && data?.title) {
          return data.title.toLowerCase().replace(/\s+/g, '-');
        }
        return value;
      },
    ],
  },
})

Text Fields

text

Single-line text input. Supports hasMany mode for storing arrays of strings (tag-style input).

OptionTypeDefaultDescription
minLengthnumber--Minimum string length.
maxLengthnumber--Maximum string length. Also sets DB column size.
hasManybooleanfalseAccept an array of strings.
minRowsnumber--Min items when hasMany: true.
maxRowsnumber--Max items when hasMany: true.
admin.autoCompletestring--HTML autocomplete attribute (e.g., 'name', 'tel').
text({ name: 'title', required: true, maxLength: 200 })

// Multiple values (tags)
text({ name: 'tags', hasMany: true, minRows: 1, maxRows: 10 })

textarea

Multi-line text input with configurable height.

OptionTypeDefaultDescription
minLengthnumber--Minimum string length.
maxLengthnumber--Maximum string length.
admin.rowsnumber3Number of visible text rows.
admin.resize'vertical' | 'horizontal' | 'both' | 'none''vertical'Resize behavior.
textarea({ name: 'description', maxLength: 1000 })

textarea({
  name: 'bio',
  admin: { rows: 5, resize: 'none' },
})

email

Specialized text input with built-in email format validation. Renders with type="email".

OptionTypeDefaultDescription
admin.autoCompletestring'email'HTML autocomplete attribute.
email({ name: 'email', required: true, unique: true })

password

Masked text input for passwords. Values should be hashed before storage using a beforeChange hook.

OptionTypeDefaultDescription
minLengthnumber8Minimum password length.
maxLengthnumber--Maximum password length.
admin.autoComplete'new-password' | 'current-password' | 'off''new-password'HTML autocomplete.
admin.showStrengthIndicatorbooleanfalseShow visual password strength meter.
password({
  name: 'password',
  required: true,
  minLength: 8,
  access: { read: () => false },
  hooks: {
    beforeChange: [
      async ({ value }) => value ? await bcrypt.hash(value, 10) : value,
    ],
  },
})

code

Code editor with syntax highlighting. Supports 25+ languages.

OptionTypeDefaultDescription
admin.languageCodeLanguage'plaintext'Syntax highlighting language.
admin.editorOptions.lineNumbersbooleantrueShow line numbers.
admin.editorOptions.wordWrapbooleanfalseEnable word wrapping.
admin.editorOptions.tabSizenumber2Tab width in spaces.
admin.editorOptions.useTabsbooleanfalseUse tabs instead of spaces.
admin.editorOptions.minHeightnumber200Minimum editor height in pixels.
admin.editorOptions.maxHeightnumber--Maximum editor height.
admin.editorOptions.fontSizenumber14Font size in pixels.
admin.editorOptions.foldingbooleantrueEnable code folding.
admin.editorOptions.matchBracketsbooleantrueHighlight matching brackets.
admin.editorOptions.autoCloseBracketsbooleantrueAuto-close brackets/quotes.

Supported languages: javascript, typescript, jsx, tsx, html, css, scss, less, json, markdown, yaml, xml, sql, graphql, python, ruby, php, java, c, cpp, csharp, go, rust, swift, kotlin, shell, bash, powershell, dockerfile, plaintext.

code({
  name: 'snippet',
  admin: {
    language: 'javascript',
    editorOptions: { lineNumbers: true, minHeight: 300 },
  },
})

Rich Content

richText

Full-featured WYSIWYG editor powered by Lexical. Content is stored as a JSON structure (Lexical editor state).

OptionTypeDefaultDescription
featuresRichTextFeature[]See belowEnabled editor features.
admin.hideToolbarbooleanfalseHide the editor toolbar.

Default features: bold, italic, underline, strikethrough, code, h1-h4, orderedList, unorderedList, indent, blockquote, link.

All available features:

CategoryFeatures
Formattingbold, italic, underline, strikethrough, code, subscript, superscript
Text StylingfontFamily, fontSize, fontColor, bgColor
Headingsh1, h2, h3, h4, h5, h6
ListsorderedList, unorderedList, checkList, indent
Links and Medialink, upload, relationship
Advancedtable, horizontalRule, codeBlock, align
Rich Mediavideo, buttonLink, collapsible, gallery
// Simple blog editor
richText({
  name: 'content',
  features: ['bold', 'italic', 'link', 'h2', 'h3', 'orderedList', 'unorderedList'],
})

// Full-featured editor
richText({
  name: 'body',
  features: [
    'bold', 'italic', 'underline', 'strikethrough', 'code',
    'h1', 'h2', 'h3', 'blockquote',
    'orderedList', 'unorderedList', 'checkList',
    'link', 'upload', 'table', 'codeBlock',
  ],
})

Numeric

number

Numeric input for integers or decimals. Supports hasMany mode for arrays of numbers.

OptionTypeDefaultDescription
minnumber--Minimum allowed value.
maxnumber--Maximum allowed value.
hasManybooleanfalseAccept an array of numbers.
minRowsnumber--Min items when hasMany: true.
maxRowsnumber--Max items when hasMany: true.
admin.stepnumber1Increment step for spinner buttons.
number({ name: 'price', required: true, min: 0, admin: { step: 0.01 } })

number({ name: 'rating', min: 1, max: 5, admin: { step: 1 } })

Selection

checkbox

Boolean true/false toggle. Also accepts the type alias 'boolean' from the Schema Builder.

OptionTypeDefaultDescription
defaultValueboolean--Initial checked state.
checkbox({ name: 'published', defaultValue: false })

checkbox({
  name: 'featured',
  label: 'Featured Post',
  admin: { position: 'sidebar' },
})

select

Dropdown for choosing from predefined options. Supports single or multi-select.

OptionTypeDefaultDescription
optionsSelectOption[]--Required. Array of { label, value } objects.
hasManybooleanfalseAllow multiple selections.
enumNamestringAutoCustom SQL enum name.
interfaceNamestring--TypeScript interface name for code generation.
filterOptionsfunction--Dynamically filter available options.
admin.isClearablebooleanfalseShow a clear button.
admin.isSortablebooleanfalseEnable drag-and-drop reorder (multi-select only).

Use the option() helper to create options quickly -- it auto-generates the value from the label:

import { select, option } from '@nextlyhq/nextly/config';

select({
  name: 'status',
  required: true,
  defaultValue: 'draft',
  options: [option('Draft'), option('Published'), option('Archived')],
})
// option('In Review') -> { label: 'In Review', value: 'in_review' }

// Multi-select
select({
  name: 'categories',
  hasMany: true,
  options: [option('Tech'), option('Business'), option('Design')],
  admin: { isClearable: true, isSortable: true },
})

radio

Radio button group for single selection. Best when all options should be visible at once.

OptionTypeDefaultDescription
optionsSelectOption[]--Required. Array of { label, value } objects.
enumNamestringAutoCustom SQL enum name.
interfaceNamestring--TypeScript interface name for code generation.
admin.layout'horizontal' | 'vertical''horizontal'Button layout direction.
radio({
  name: 'priority',
  options: [option('Low'), option('Medium'), option('High')],
  admin: { layout: 'horizontal' },
})

date

Date and/or time picker. Dates are stored in UTC as ISO 8601 strings.

OptionTypeDefaultDescription
admin.date.pickerAppearance'dayOnly' | 'dayAndTime' | 'timeOnly' | 'monthOnly''dayOnly'Picker mode.
admin.date.displayFormatstring--Display format string (date-fns format).
admin.date.monthsToShow1 | 21Number of months visible in the picker.
admin.date.minDateDate | string--Earliest selectable date.
admin.date.maxDateDate | string--Latest selectable date.
admin.date.minTimeDate | string--Earliest selectable time.
admin.date.maxTimeDate | string--Latest selectable time.
admin.date.timeIntervalsnumber30Time step in minutes.
admin.date.timeFormatstring'h:mm aa'Time display format.
date({ name: 'publishedAt' })

// Date and time
date({
  name: 'eventStart',
  admin: {
    date: { pickerAppearance: 'dayAndTime', timeIntervals: 15 },
  },
})

// Time only
date({
  name: 'openingTime',
  admin: {
    date: { pickerAppearance: 'timeOnly', timeFormat: 'HH:mm' },
  },
})

Relationships

relationship

Reference documents from other collections. Supports single, multi, and polymorphic relationships.

OptionTypeDefaultDescription
relationTostring | string[]--Required. Target collection slug(s).
hasManybooleanfalseAllow multiple document references.
minRowsnumber--Min selections when hasMany: true.
maxRowsnumber--Max selections when hasMany: true.
maxDepthnumber1Population depth for nested relationships.
filterOptionsobject | function--Filter available documents.
admin.allowCreatebooleantrueAllow creating new documents from the field.
admin.allowEditbooleantrueAllow editing related documents.
admin.isSortablebooleantrueEnable drag-and-drop reorder (multi only).
admin.sortOptionsstring | object--Default sort. Prefix with - for descending.
admin.appearance'select' | 'drawer''select'Picker UI style. Use 'drawer' for large lists.
// Single relationship
relationship({ name: 'author', relationTo: 'users', required: true })

// Has many
relationship({
  name: 'categories',
  relationTo: 'categories',
  hasMany: true,
  maxRows: 5,
})

// Polymorphic (multiple target collections)
relationship({
  name: 'relatedContent',
  relationTo: ['posts', 'pages', 'products'],
  hasMany: true,
})

// With dynamic filter
relationship({
  name: 'parent',
  relationTo: 'pages',
  filterOptions: ({ id }) => {
    if (id) return { id: { not_equals: id } };
    return true;
  },
})

upload

Reference files from upload-enabled collections. Works like relationship but with media-specific features like thumbnails and file filtering.

OptionTypeDefaultDescription
relationTostring | string[]--Required. Upload collection slug(s).
hasManybooleanfalseAllow multiple file selections.
minRowsnumber--Min uploads when hasMany: true.
maxRowsnumber--Max uploads when hasMany: true.
maxDepthnumber1Population depth.
maxFileSizenumber--Max file size in bytes.
mimeTypesstring--Allowed MIME types (e.g., 'image/*').
filterOptionsobject | function--Filter by mimeType, filesize, width, height, filename, alt.
admin.allowCreatebooleantrueAllow uploading new files from the field.
admin.allowEditbooleantrueAllow editing upload metadata.
admin.isSortablebooleantrueDrag-and-drop reorder (multi only).
admin.displayPreviewbooleantrueShow thumbnail preview.
// Single image
upload({ name: 'featuredImage', relationTo: 'media' })

// Gallery with constraints
upload({
  name: 'gallery',
  relationTo: 'media',
  hasMany: true,
  maxRows: 10,
  filterOptions: { mimeType: { contains: 'image' } },
})

// Polymorphic uploads
upload({
  name: 'attachment',
  relationTo: ['media', 'documents'],
})

Structured Fields

array

Repeatable sets of fields. Each row contains the same field structure. Rows can be added, removed, and reordered.

OptionTypeDefaultDescription
fieldsFieldConfig[]--Required. Field definitions for each row.
minRowsnumber--Minimum number of rows.
maxRowsnumber--Maximum number of rows.
labels{ singular?, plural? }--Custom labels (e.g., 'Link' / 'Links').
interfaceNamestring--TypeScript interface name.
dbNamestringAutoCustom database table name.
virtualbooleanfalseNo database storage.
admin.initCollapsedbooleanfalseStart rows collapsed.
admin.isSortablebooleantrueEnable drag-and-drop reorder.
admin.components.RowLabelReact.ComponentType--Custom row label component.
array({
  name: 'links',
  labels: { singular: 'Link', plural: 'Links' },
  maxRows: 10,
  fields: [
    text({ name: 'label', required: true }),
    text({ name: 'url', required: true }),
  ],
})

// FAQ section
array({
  name: 'faq',
  labels: { singular: 'Question', plural: 'Questions' },
  fields: [
    text({ name: 'question', required: true }),
    richText({ name: 'answer', required: true }),
  ],
  admin: { initCollapsed: true },
})

group

Organize related fields together. Named groups create nested data structures. Groups without a name are presentational only.

OptionTypeDefaultDescription
namestring--If provided, fields are nested under this property. If omitted, purely visual.
fieldsFieldConfig[]--Required. Nested field definitions.
interfaceNamestring--TypeScript interface name.
dbNamestringAutoCustom database column name.
virtualbooleanfalseNo database storage.
admin.hideGutterbooleanfalseRemove the left-side visual gutter.
// Named group -- creates nested data: { seo: { metaTitle, metaDescription } }
group({
  name: 'seo',
  fields: [
    text({ name: 'metaTitle', maxLength: 60 }),
    textarea({ name: 'metaDescription', maxLength: 160 }),
  ],
})

// Presentational group -- no data nesting, fields live at parent level
group({
  label: 'Display Settings',
  fields: [
    checkbox({ name: 'showTitle', defaultValue: true }),
    checkbox({ name: 'showDate', defaultValue: true }),
  ],
  admin: { hideGutter: true },
})

json

Store arbitrary JSON data with an in-browser code editor. Supports JSON Schema validation.

OptionTypeDefaultDescription
jsonSchemaJSONSchemaDefinition--Inline JSON Schema for validation and editor hints.
admin.editorOptions.heightnumber | string300Editor height.
admin.editorOptions.minHeightnumber100Minimum height.
admin.editorOptions.maxHeightnumber--Maximum height.
admin.editorOptions.lineNumbersbooleantrueShow line numbers.
admin.editorOptions.foldingbooleantrueEnable code folding.
admin.editorOptions.wordWrapbooleanfalseEnable word wrap.
admin.editorOptions.minimapbooleanfalseShow code minimap.
admin.editorOptions.tabSizenumber2Tab size.
admin.editorOptions.formatOnBlurbooleantrueAuto-prettify on blur.
admin.editorOptions.validateOnChangebooleantrueReal-time syntax validation.

Database storage: PostgreSQL uses JSONB, MySQL uses JSON, SQLite uses TEXT.

json({ name: 'metadata' })

// With JSON Schema validation
json({
  name: 'settings',
  jsonSchema: {
    type: 'object',
    properties: {
      theme: { type: 'string', enum: ['light', 'dark'] },
      maxItems: { type: 'number', minimum: 1 },
    },
    required: ['theme'],
  },
})

Component Fields

component

Embed reusable Components within Collections, Singles, or other Components. Three modes are available:

OptionTypeDefaultDescription
componentstring--Single mode: one specific Component slug. Mutually exclusive with components.
componentsstring[]--Dynamic zone: array of allowed Component slugs. Mutually exclusive with component.
repeatablebooleanfalseAllow multiple instances (array mode).
minRowsnumber--Min instances when repeatable: true.
maxRowsnumber--Max instances when repeatable: true.
admin.initCollapsedbooleanfalseStart instances collapsed.
admin.isSortablebooleantrueDrag-and-drop reorder (repeatable only).
// Fixed single component
component({ name: 'seo', component: 'seo' })

// Dynamic zone -- editors pick from multiple types
component({
  name: 'layout',
  components: ['hero', 'cta', 'content-block'],
  repeatable: true,
})

// Repeatable single component
component({
  name: 'features',
  component: 'feature-card',
  repeatable: true,
  minRows: 1,
  maxRows: 12,
})

Virtual Fields

join

Display reverse relationships -- entries from another collection that reference the current document. Join fields are read-only and do not store data. They query at read time.

OptionTypeDefaultDescription
collectionstring--Required. Collection containing the referencing field.
onstring--Required. Field name that references this collection. Supports dot notation.
whereobject--Additional filter for joined entries.
defaultLimitnumber10Max entries to display.
defaultSortstring--Sort field. Prefix with - for descending.
maxDepthnumber1Population depth for relationships in joined entries.
admin.allowNavigationbooleantrueMake entries clickable links.
admin.defaultColumnsstring[]--Columns to display in the list.
admin.allowCreatebooleanfalseShow "Create New" button.
// On a Categories collection -- show all posts in this category
{
  name: 'posts',
  type: 'join',
  label: 'Posts in this Category',
  collection: 'posts',
  on: 'category',
  defaultSort: '-createdAt',
  admin: {
    defaultColumns: ['title', 'status', 'createdAt'],
  },
}

Helper Utilities

option()

Creates a { label, value } option for select and radio fields. Auto-generates the value from the label by lowercasing and replacing spaces with underscores.

import { option } from '@nextlyhq/nextly/config';

option('Draft')         // { label: 'Draft', value: 'draft' }
option('In Progress')   // { label: 'In Progress', value: 'in_progress' }
option('Active', 'active')  // { label: 'Active', value: 'active' }

Next Steps

  • Collections -- define content types using fields
  • Singles -- define single-document content using fields
  • Components -- create reusable field groups