> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hiveku.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Link Collections with References

> Use reference fields to link entries across CMS collections — like blog posts to authors or products to categories

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](/cms/manifest), a reference field looks like:

```json theme={null}
{
  "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:

```json theme={null}
{
  "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"`:

```json theme={null}
{
  "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:

```json theme={null}
{
  "name": "author",
  "type": "reference",
  "collection": "authors"
}
```

```json theme={null}
"author": "jane-doe"
```

An array reference stores a list of slugs:

```json theme={null}
{
  "name": "relatedPosts",
  "type": "reference",
  "collection": "blog",
  "multiple": true
}
```

```json theme={null}
"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`:

```json theme={null}
{
  "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:

```tsx theme={null}
// 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:

```tsx theme={null}
// 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>
  );
}
```

<Tip>
  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."*
</Tip>

### 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:

```tsx theme={null}
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.

```json theme={null}
// 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:

```tsx theme={null}
// 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.

```json theme={null}
{ "name": "category", "type": "reference", "collection": "categories" }
```

### Product → Tags (multi ref)

A product can be tagged with multiple categories. Use `multiple: true`.

```json theme={null}
{ "name": "tags", "type": "reference", "collection": "categories", "multiple": true }
```

For tag-based filtering on the storefront:

```tsx theme={null}
const products = await listEntries('products', {
  where: { tags: { contains: 'electronics' } },
});
```

### Post → Related Posts (self ref)

A blog post can reference other blog posts:

```json theme={null}
{
  "name": "relatedPosts",
  "type": "reference",
  "collection": "blog",
  "multiple": true,
  "displayField": "title"
}
```

The picker excludes the current entry automatically so a post can't reference itself.

### Case Study → Featured Products

A case study mentions specific products. Use a multi-reference:

```json theme={null}
{
  "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.
```

<Warning>
  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.
</Warning>

## Verifying References

A few quick checks after adding a reference field:

<Steps>
  <Step title="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.
  </Step>

  <Step title="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).
  </Step>

  <Step title="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.
  </Step>
</Steps>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Picker says 'No matching entries'">
    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.
  </Accordion>

  <Accordion title="Manifest validation: 'reference points at missing collection'">
    The `collection` value on a reference field must match an existing collection's `id`. Check spelling — `author` and `authors` are different.
  </Accordion>

  <Accordion title="Reference shows a slug instead of the entry's name on the page">
    Your component is rendering the slug directly. Either resolve the reference (Option B above) or use a populate helper.
  </Accordion>

  <Accordion title="Saved value is the wrong format">
    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.
  </Accordion>

  <Accordion title="The picker shows the wrong field">
    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`.
  </Accordion>
</AccordionGroup>

## What's Next?

<CardGroup cols={2}>
  <Card title="Field Types" icon="list" href="/cms/field-types">
    See every field type, including reference details
  </Card>

  <Card title="Collections" icon="layer-group" href="/cms/collections">
    Patterns for collections that commonly reference each other
  </Card>

  <Card title="Editing Content" icon="pencil" href="/cms/editing-content">
    The picker UI in context
  </Card>

  <Card title="AI Integration" icon="wand-magic-sparkles" href="/cms/agent-integration">
    Ask the AI to wire up reference resolution
  </Card>
</CardGroup>
