Show navigationHide navigation
Select
Dropdown (single choice) or checkbox list (multiple choices) from a fixed set of options. Use for categories, statuses, roles, and any constrained value.
Usage#
typescriptcategory: { label: 'Category', format: 'select', options: [ { label: 'Design', value: 'design' }, { label: 'Engineering', value: 'engineering' }, { label: 'Marketing', value: 'marketing' }, ] as const, },
Use
as conston theoptionsarray for literal type inference —query()will return'design' | 'engineering' | 'marketing'instead ofstring.
Options#
| Option | Type | Default | Description |
|---|---|---|---|
options | readonly { label, value }[] | — | Required. At least one option. Values must be unique. |
multiple | boolean | false | Enable multiselect (checkbox list instead of dropdown) |
required | boolean | false | Must have a selection |
defaultOption | string | — | Default value for new entries (single select only) |
defaultOptions | string[] | — | Default values for new entries (multiselect only) |
hint | string | — | Helper text below the field |
When single select is optional (not required), the dropdown includes a — empty option.
Multiselect#
Set multiple: true for a checkbox list where editors can pick zero or more values:
typescriptfeatures: { label: 'Features', format: 'select', multiple: true, options: [ { label: 'Dark Mode', value: 'dark-mode' }, { label: 'Search', value: 'search' }, { label: 'RSS Feed', value: 'rss' }, { label: 'Analytics', value: 'analytics' }, ] as const, defaultOptions: ['search'], },
Example: Blog post categories#
Schema — posts with a required category and optional feature tags:
typescript// cms/octocms.config.ts post: { label: 'Post', hasMany: true, fields: { title: { label: 'Title', format: 'string', entryTitle: true, required: true }, category: { label: 'Category', format: 'select', required: true, options: [ { label: 'Tutorial', value: 'tutorial' }, { label: 'Case Study', value: 'case-study' }, { label: 'News', value: 'news' }, ] as const, }, tags: { label: 'Tags', format: 'select', multiple: true, options: [ { label: 'React', value: 'react' }, { label: 'Next.js', value: 'nextjs' }, { label: 'TypeScript', value: 'typescript' }, ] as const, }, }, },
Query + render:
tsx// src/app/blog/page.tsx import { query } from 'cms/__generated__/query'; export default async function BlogPage() { // Filter by category const tutorials = await query('post') .filter((p) => p.fields.category === 'tutorial') .toArray(); // Filter by multiselect tag const reactPosts = await query('post') .filter((p) => p.fields.tags?.includes('react')) .toArray(); return ( <> <h2>Tutorials ({tutorials.length})</h2> {tutorials.map((p) => ( <div key={p.sys.id}> <h3>{p.fields.title}</h3> <span className="badge">{p.fields.category}</span> {p.fields.tags?.map((tag) => ( <span key={tag} className="tag">{tag}</span> ))} </div> ))} </> ); }
Storage#
Single select — plain string matching an option value:
json{ "category": "tutorial" }
Multiselect — native string[] array:
json{ "tags": ["react", "typescript"] }
Query result#
Single select: string. Multiselect: string[]. With as const on options, the type is a literal union (e.g. 'tutorial' | 'case-study' | 'news').