nextjs-cms

Sections

A section is one content type. It defines a form, a database table, admin pages, and permissions, all from one file.

Choosing a section type

TypeHelperRowsAdmin pagesUse it for
SimplesimpleSection()Exactly oneA single edit formSite settings, contact info, homepage content, legal pages
Has-itemshasItemsSection()ManyList (search, pagination) + create + editPosts, products, jobs, FAQs, team members, anything repeatable
CategorycategorySection()Many, as a treeTree browser + create + editTaxonomies, nested menus, product categories
import { categorySection, hasItemsSection, simpleSection } from 'nextjs-cms/core/sections'

Anatomy of a section file

sections/faq.section.ts
import { hasItemsSection } from 'nextjs-cms/core/sections'
import { richTextField, textField } from 'nextjs-cms/core/fields'

// 1. Define fields as variables so other options can reference them.
const question = textField({ name: 'question', required: true })
const answer = richTextField({ name: 'answer', required: true })

// 2. Default-export exactly one section.
export default hasItemsSection({
    name: 'faq', // unique key
    order: 5, // sidebar position
    icon: 'circle-help', // Lucide icon
    title: { section: 'FAQ', singular: 'Question', plural: 'Questions' },
    headingField: question, // a field object, not a string
    db: { table: 'faq' },
    fields: [question, answer],
})
Tip:

Options like headingField, coverPhotoField, search.searchFields, and db.unique take the field object you created, not its name. Declaring fields as variables first is the idiomatic way to write sections: it keeps references type-safe and renames safe.

Options shared by every section type

OptionTypeDefaultDescription
namestringrequiredUnique section key (case-insensitive). Used in URLs, permissions, logs, and upload folders.
ordernumberrequiredSort position in the admin sidebar.
iconLucide icon namenoneSidebar icon, e.g. 'newspaper', 'settings', 'folder-tree'.
readonlybooleanfalseReserved. Accepted and stored on the section, but not enforced by the admin yet. To stop edits today, control it with permissions or a beforeUpdate hook.
dbobjectrequiredTable name plus identifier, keys and indexes. See Database Options. Simple sections only accept db.table.
fieldsFieldConfig[] or FieldGroupConfig[]requiredThe form. Either all fields, or all field groups. At least one.
hooksHooksnoneFunctions run around create, update and delete. See Hooks.
galleryobjectnoneAdds a multi-photo gallery stored in its own table. See Photo Gallery.

Each type adds its own options (title, headingField, depth, search, ...), documented on its page.

Naming rules

nextjs-cms validates names when sections load, and update-sections refuses to run on these mistakes:

  • Section names must be unique across the project, ignoring case.
  • Field names must be unique within a section, ignoring case. They become column names, so use snake_case or camelCase consistently.
  • id is reserved for the automatic identifier. Use a different name, or make your field the identifier.
  • locale is reserved as a column name (it's used by translation tables).
  • Table names must not end in _locales. That suffix is reserved for translation tables.

Columns you get for free

Besides your fields, every section table gets bookkeeping columns:

ColumnSimpleHas-items / Category
created_at—✓
updated_at✓✓
created_by—✓ (username of the admin)
updated_by✓✓

Category sections with depth > 1 also get parent_id and level. See Category Section.

How a section becomes a table

  1. You save sections/faq.section.ts.
  2. You run pnpm nextjs-cms --dev update-sections.
  3. The CLI loads every section, builds the table definition each implies, and compares it with MySQL.
  4. For each difference it acts or asks: create a new table, add a column, detect a rename (it asks "new field or rename of X?"), change a column type, or remove a column that is no longer in your config (it asks first).
  5. It regenerates dynamic-schemas/schema.ts if Drizzle schema generation is on, and grants the Master admin access to new sections.

See the CLI reference for flags and prompts.