Show navigationHide navigation
Reference
Links one entry to another — authors on a post, posts on a blog page, related products. In the editor: sortable drag-and-drop list with search, inline editing, and "create new" flow.
Usage#
typescriptauthors: { label: 'Authors', format: 'reference', required: true, reference: { collections: ['author'], cardinality: 'many', min: 1, max: 5, }, },
Options#
| Option | Type | Default | Description |
|---|---|---|---|
required | boolean | false | Must have at least one reference (or min if higher) |
reference.collections | string[] | All collections | Which collections can be referenced |
reference.cardinality | 'one' | 'many' | 'many' | Single object or array of objects |
reference.min | number | — | Minimum items (for many) |
reference.max | number | — | Maximum items (for many) |
hint | string | — | Helper text below the field |
Cardinality#
Many-to-many (default)#
typescriptposts: { label: 'Posts', format: 'reference', reference: { collections: ['post'], cardinality: 'many' }, },
Stored as an array of reference keys. Resolved by query() to an array of full entry objects.
One-to-one#
typescriptauthor: { label: 'Author', format: 'reference', reference: { collections: ['author'], cardinality: 'one' }, },
Stored as a single reference key string. Resolved to a single entry object (not wrapped in an array).
Example: Blog with authors#
Schema — posts linked to authors, where each author has roles:
typescript// cms/octocms.config.ts collections: { role: { label: 'Role', hasMany: true, fields: { title: { label: 'Title', format: 'string', entryTitle: true, required: true }, }, }, author: { label: 'Author', hasMany: true, fields: { name: { label: 'Name', format: 'string', entryTitle: true, required: true }, avatar: { label: 'Avatar', format: 'image' }, roles: { label: 'Roles', format: 'reference', reference: { collections: ['role'], cardinality: 'many' }, }, }, }, post: { label: 'Post', hasMany: true, fields: { title: { label: 'Title', format: 'string', entryTitle: true, required: true }, author: { label: 'Author', format: 'reference', required: true, reference: { collections: ['author'], cardinality: 'one' }, }, body: { label: 'Body', format: 'markdown' }, }, }, },
Query + render:
tsx// src/app/blog/[slug]/page.tsx import Image from 'next/image'; import { query } from 'cms/__generated__/query'; export default async function PostPage({ params }: { params: { slug: string } }) { const post = await query('post').filter({ slug: params.slug }).first(); if (!post) return null; const author = post.fields.author; // author is a resolved entry: { sys: {...}, fields: { name, avatar, roles } } // author.fields.roles is an array of resolved role entries return ( <article> <h1>{post.fields.title}</h1> <div className="author"> {author.fields.avatar && ( <Image src={author.fields.avatar.src} alt={author.fields.avatar.alt} width={48} height={48} className="rounded-full" /> )} <span>{author.fields.name}</span> {author.fields.roles?.map((role) => ( <span key={role.sys.id} className="badge">{role.fields.title}</span> ))} </div> </article> ); }
Deep resolution#
References are resolved recursively. A post → author → roles chain produces fully nested objects:
json{ "sys": { "id": "p1", "type": "post" }, "fields": { "title": "My Post", "author": { "sys": { "id": "a1", "type": "author" }, "fields": { "name": "Alice", "roles": [ { "sys": { "id": "r1", "type": "role" }, "fields": { "title": "Editor" } } ] } } } }
Inline editing#
Click a referenced item's title or pencil icon to edit it inline:
- Opens an overlay panel with the referenced entry's full editor.
- Stack is encoded in the URL as repeated
?overlay=<content-path>params, so a deep link round-trips when pasted into a new window. - Overlays stack infinitely; cycle detection prevents opening an entry already in the stack.
- The browser back button closes one overlay (each push adds a history entry).
- Saves are independent per entry. When an overlay closes after a save, the parent re-resolves titles, backlinks, and history.
- Deleting a referenced entry removes it from the parent list and shows backlink warnings.
See ../inline-editing-overlays.md for the full architecture.
Storage#
Many — JSON array of reference keys:
json{ "roles": "[\"role-r1.json\",\"role-r2.json\"]" }
One — single reference key string:
json{ "author": "author-a1.json" }
Query result#
Many — array of resolved entry objects. One — single resolved entry object.
All nested references and images within the referenced entries are also resolved.