Skip to main content
A reference field is how one CMS collection points at another. It keeps content normalized — author info lives in one place, blog posts link to it — and gives editors a searchable picker instead of free-text retyping. This guide covers how references work, the picker UI, single vs array references, and how to render referenced data on the page.

How References Work

In the manifest, a reference field looks like:
{
  "name": "author",
  "type": "reference",
  "collection": "authors",
  "required": true
}
When an entry is saved, the field stores the slug of the referenced entry — for example, "author": "jane-doe" (assuming the authors collection has an entry at content/authors/jane-doe.json). The CMS panel renders a searchable picker for the field. Users type the author’s name; matching entries surface; click to select. When your component reads the blog entry, it can either:
  • Use the slug directly (cheap, fine for most cases)
  • Read the referenced entry’s full data (a small additional read at build/request time)

Setting Up a Reference

1. Make sure both collections exist

The target collection (the one being referenced) needs to exist in the manifest first. If you don’t have an authors collection yet, add one:
{
  "id": "authors",
  "name": "Authors",
  "path": "content/authors",
  "format": "json",
  "slugFrom": "field:name",
  "fields": [
    { "name": "name", "type": "string", "required": true },
    { "name": "bio", "type": "markdown" },
    { "name": "avatar", "type": "image" },
    { "name": "twitter", "type": "url" }
  ]
}
Then add at least one entry — the picker won’t be useful with zero options.

2. Add the reference field to the source collection

In the source collection (the one that points), add a field with type: "reference":
{
  "name": "author",
  "type": "reference",
  "collection": "authors",
  "required": true
}

3. Pick a value in the CMS panel

Open any entry in the source collection. The reference field renders as a search box. Type to filter, click to pick.

Single vs Array References

A single reference stores one slug:
{
  "name": "author",
  "type": "reference",
  "collection": "authors"
}
"author": "jane-doe"
An array reference stores a list of slugs:
{
  "name": "relatedPosts",
  "type": "reference",
  "collection": "blog",
  "multiple": true
}
"relatedPosts": ["welcome", "getting-started", "advanced-tips"]
In the CMS panel, multi-references show as removable chips above the picker. Reorder by dragging.

Customizing the Picker

The picker shows the target entry’s title or name field by default. You can override with displayField:
{
  "name": "category",
  "type": "reference",
  "collection": "categories",
  "displayField": "label"
}
Now the picker shows each category’s label field instead of title.

Rendering Referenced Data

Your code reads the source entry’s slug for the reference, then optionally fetches the target entry to render its data.

Option A: Use just the slug

If you only need the slug (for a link), no extra read needed:
// app/blog/[slug]/page.tsx
import { readEntry } from '@/lib/cms';

export default async function BlogPostPage({ params }) {
  const post = await readEntry('blog', params.slug);

  return (
    <article>
      <h1>{post.title}</h1>
      <a href={`/authors/${post.author}`}>By {post.author}</a>
      {/* ... */}
    </article>
  );
}

Option B: Fetch the referenced entry

For richer rendering — name, avatar, bio — fetch the target entry too:
// app/blog/[slug]/page.tsx
import { readEntry } from '@/lib/cms';

export default async function BlogPostPage({ params }) {
  const post = await readEntry('blog', params.slug);
  const author = await readEntry('authors', post.author);

  return (
    <article>
      <h1>{post.title}</h1>
      <div className="author">
        <img src={author.avatar} alt={author.name} />
        <div>
          <p>{author.name}</p>
          <p>{author.bio}</p>
        </div>
      </div>
      {/* ... */}
    </article>
  );
}
Ask the AI to do this refactor for you: “Update the blog detail page to render the author’s name, avatar, and bio inline using the author reference.”

Option C: Resolve at the helper level

For larger sites with many references, ask the AI to add a populate option to the CMS helper:
const post = await readEntry('blog', params.slug, {
  populate: ['author', 'relatedPosts'],
});

// post.author is now the full author object, not just a slug
console.log(post.author.name, post.author.avatar);

Common Reference Patterns

Blog ↔ Author

A blog post has one author. The author has a profile page and many posts.
// In the blog collection's fields:
{ "name": "author", "type": "reference", "collection": "authors", "required": true }
For the author’s profile page, list their posts by querying the blog collection with a filter:
// app/authors/[slug]/page.tsx
const author = await readEntry('authors', params.slug);
const posts = await listEntries('blog', {
  where: { author: params.slug },
  sort: 'publishedAt desc',
});

Product → Category (single ref)

Each product belongs to one category. Categories have their own listing pages.
{ "name": "category", "type": "reference", "collection": "categories" }

Product → Tags (multi ref)

A product can be tagged with multiple categories. Use multiple: true.
{ "name": "tags", "type": "reference", "collection": "categories", "multiple": true }
For tag-based filtering on the storefront:
const products = await listEntries('products', {
  where: { tags: { contains: 'electronics' } },
});

Post → Related Posts (self ref)

A blog post can reference other blog posts:
{
  "name": "relatedPosts",
  "type": "reference",
  "collection": "blog",
  "multiple": true,
  "displayField": "title"
}
The picker excludes the current entry automatically so a post can’t reference itself. A case study mentions specific products. Use a multi-reference:
{
  "name": "featuredProducts",
  "type": "reference",
  "collection": "products",
  "multiple": true
}

Dangling References

If you delete an entry that’s referenced by others, the reference becomes dangling — the source still has a slug, but the target is gone. The CMS panel handles this gracefully:
  • Source entries continue to save
  • The picker shows a warning chip (“Missing: jane-doe”) for the dangling reference
  • Your code should treat a missing target as nullable (if (!author) return null;)
To clean up dangling references, ask the AI:
Find any blog posts whose author reference points at a deleted author.
Either set them to a default author or unset the field.
Bulk-deleting a frequently-referenced collection (e.g. removing 50 authors who are referenced by 500 posts) creates a lot of dangling refs. Migrate or repoint references before deleting.

Verifying References

A few quick checks after adding a reference field:
1

Picker opens with results

Open an entry in the source collection. Click the reference field. The picker should list entries from the target collection.If the picker is empty, the target collection has no entries — add at least one.
2

Saving stores the slug

Pick a value, save, then open the file in the code editor. The frontmatter (or JSON value) should show "author": "jane-doe" (the target slug).
3

Code can resolve the target

Ask the AI: “Render the author’s name on the blog detail page using the reference.” Verify the live preview shows the linked data.

Troubleshooting

Either the target collection is empty, or the search term doesn’t match any entry’s display field. Clear the search to see the full list.
The collection value on a reference field must match an existing collection’s id. Check spelling — author and authors are different.
Your component is rendering the slug directly. Either resolve the reference (Option B above) or use a populate helper.
Single references store a string slug; multi-references store an array of slugs. If your downstream code expects one format and gets another, double-check multiple: true is set or unset correctly.
Set displayField on the reference to the field you want shown — e.g. "displayField": "name" for an authors collection where the title field is name.

What’s Next?

Field Types

See every field type, including reference details

Collections

Patterns for collections that commonly reference each other

Editing Content

The picker UI in context

AI Integration

Ask the AI to wire up reference resolution