nextjs-cms

Fields

Fields are the inputs of a section's form and the columns of its table. nextjs-cms ships 17 field types.

Every field is created with a helper function from nextjs-cms/core/fields:

import { textField } from 'nextjs-cms/core/fields'

const title = textField({ name: 'title', required: true })

The helper validates its options immediately (a typo like maxLenght throws when the section file loads, naming the field) and returns a plain config object you pass to a section's fields.

All field types

HelperInputMySQL columnPage
textFieldSingle-line textVARCHAR(maxLength ?? 255)textField
textAreaFieldMulti-line textLONGTEXTtextAreaField
richTextFieldWYSIWYG editor (HTML)LONGTEXTrichTextField
slugFieldURL slug, generated from a text fieldVARCHAR(255)slugField
passwordFieldPassword, stored bcrypt-hashedVARCHAR(255)passwordField
numberFieldNumber (int, float, double, bigint, auto-increment)TINYINT … BIGINT, FLOAT, DOUBLEnumberField
checkboxFieldSwitchBOOLEANcheckboxField
colorFieldColor pickerCHAR(7)colorField
dateFieldDate, date-time, or timestamp pickerDATE, DATETIME, TIMESTAMPdateField
dateRangeFieldStart and end datetwo DATE/DATETIME columnsdateRangeField
selectFieldDropdown (static, DB, or section options)ENUM, INT, or VARCHAR(255)selectField
selectMultipleFieldMulti-selectVARCHAR(255) or a join tableselectMultipleField
tagsFieldFree-form tagsLONGTEXT or a join tabletagsField
mapFieldGoogle Maps location pickerVARCHAR(200)mapField
photoFieldImage upload, resized with sharpVARCHAR(50) (file name)photoField
videoFieldVideo uploadVARCHAR(50) (file name)videoField
documentFieldPDF, Office, text, CSV uploadVARCHAR(50) (file name)documentField

Columns are NOT NULL when the field is required (and not conditional), otherwise nullable. Text columns use utf8mb4 with utf8mb4_unicode_ci.

Options every field accepts

OptionTypeDefaultDescription
namestringrequiredColumn name and form key. Unique within the section.
labelstring | Record<locale, string>from nameForm label. 'published_at' becomes "Published At" when omitted.
requiredbooleanfalseReject empty values; make the column NOT NULL.
defaultValuedepends on the fieldnoneInitial value of the form and the column's DEFAULT.
ordernumbernoneSort position within the form or group.
conditionalRulesrule array or and() / or()noneShow (and save) the field only when rules match.
adminGeneratedtrue | false | 'readonly'trueWhether editors fill the field in.
localizedbooleanfalseStore a translation per locale.

Each is explained with examples in Common Field Options.

Reusing a field across options

A field config is a value. Declare it once and reference it wherever a section option needs a field:

const title = textField({ name: 'title', required: true })
const slug = slugField({ name: 'slug', for: title })               // slug source
const cover = photoField({ name: 'cover' })

export default hasItemsSection({
    headingField: title,                                             // list title
    coverPhotoField: cover,                                          // list thumbnail
    search: { searchFields: [title] },                               // search box
    db: { table: 'posts', unique: [{ columns: [slug] }] },           // constraint
    fields: [title, slug, cover],
    // ...
})

How a field is saved

On every create or update, each field goes through the same steps:

  1. Visibility: if the field has conditionalRules and they don't match the submitted values, the field is skipped entirely (not validated, not saved).
  2. Required check: required fields must have a non-empty value.
  3. Preparation: type-specific validation and transformation. Length and range checks, slug normalization, password hashing, image decoding and resizing, rich-text sanitizing.
  4. Uniqueness: db.unique constraints are checked against the table.
  5. Write: the column values are inserted or updated.
  6. Post-submit: files are written to disk, join tables (destinationDb) are rewritten, inline editor images are saved. If this step fails, the write is rolled back.