Field Groups
Split long forms into titled cards. Groups are purely visual; they don't change the table.
import { fieldGroup } from 'nextjs-cms/core/fields'Basic usage
import { hasItemsSection } from 'nextjs-cms/core/sections'
import { dateRangeField, fieldGroup, mapField, richTextField, textField } from 'nextjs-cms/core/fields'
const title = textField({ name: 'title', required: true })
export default hasItemsSection({
name: 'events',
order: 3,
title: { section: 'Events', singular: 'Event', plural: 'Events' },
headingField: title,
db: { table: 'events' },
fields: [
fieldGroup({
title: 'Details',
fields: [title, richTextField({ name: 'description' })],
}),
fieldGroup({
title: 'When & where',
fields: [
dateRangeField({ startName: 'starts_at', endName: 'ends_at', format: 'datetime', required: true }),
textField({ name: 'venue' }),
mapField({ name: 'location' }),
],
}),
],
})Each group renders as a separate card with its title. The table still gets one column per field.
Options
| Option | Type | Default | Description |
|---|---|---|---|
title | string | Record<locale, string> | none | Card heading. Translatable: { en: 'Details', ar: 'التفاصيل' }. |
fields | FieldConfig[] | required | Fields in this group. Groups can't be nested. |
order | number | none | Sort position among groups. |
Rules
All or nothing. A section's fields array holds either only fields or only groups, never a mix. If you want some fields "ungrouped", put them in a group without a title:
fields: [
fieldGroup({ fields: [title, slug] }), // no title: renders without a heading
fieldGroup({ title: 'SEO', fields: [metaTitle, metaDescription] }),
]A field lives in one group. Field names must be unique across the whole section, groups included.
Referencing grouped fields still works. headingField, coverPhotoField, search.searchFields, db.unique, and conditionalRules can point at fields inside any group, because you pass the field object:
const title = textField({ name: 'title', required: true })
const cover = photoField({ name: 'cover' })
export default hasItemsSection({
// ...
headingField: title,
coverPhotoField: cover,
fields: [
fieldGroup({ title: 'Text', fields: [title] }),
fieldGroup({ title: 'Media', fields: [cover] }),
],
})Reusable groups
Because groups are plain values, you can build them in a helper and share them between sections:
import { fieldGroup, textAreaField, textField } from 'nextjs-cms/core/fields'
export function seoGroup() {
return fieldGroup({
title: { en: 'SEO', ar: 'تحسين محركات البحث' },
fields: [
textField({ name: 'meta_title', label: 'Meta title', maxLength: 70, localized: true }),
textAreaField({ name: 'meta_description', label: 'Meta description', maxLength: 160, localized: true }),
],
})
}import { seoGroup } from './shared/seo'
// ...
fields: [fieldGroup({ title: 'Content', fields: [title, body] }), seoGroup()],Call a factory function (seoGroup()) rather than exporting one shared object. Each section then gets its own field instances, so nothing leaks between sections.