---
title: "EmDash Custom Fields: A Practical Reference"
description: "How to define and use custom fields in EmDash collections. Field types, validation, and querying patterns."
article_type: child
canonical: https://dashstro.com/learn/emdash-custom-fields
---
When I started building with EmDash, the field system was the first thing I had to get right. Get it wrong and you're fighting the CMS instead of shipping content. Get it right and everything — queries, templates, admin UI — clicks into place automatically.

EmDash supports 11 field types. You define them in `seed/seed.json` inside a collection's `fields` array. Once defined, they appear in the admin UI automatically and are queryable in your Astro pages via typed access on `entry.data`.

## Defining Fields in seed.json

Each field lives in the `fields` array of a collection definition. At minimum you need `slug`, `label`, and `type`. Everything else is optional.

```json
{
  "collections": [
    {
      "name": "products",
      "label": "Products",
      "fields": [
        {
          "slug": "tagline",
          "label": "Tagline",
          "type": "string",
          "required": true
        },
        {
          "slug": "price",
          "label": "Price (USD)",
          "type": "number",
          "min": 0
        },
        {
          "slug": "in_stock",
          "label": "In Stock",
          "type": "boolean"
        },
        {
          "slug": "tier",
          "label": "Tier",
          "type": "select",
          "options": ["free", "pro", "enterprise"]
        },
        {
          "slug": "featured_image",
          "label": "Featured Image",
          "type": "image"
        },
        {
          "slug": "body",
          "label": "Body",
          "type": "portableText"
        }
      ]
    }
  ]
}
```

## All 11 Field Types at a Glance

Here's the complete reference. The runtime shape column tells you what you'll actually get when you access `entry.data.fieldName` in your templates.

| Type | Runtime Shape | Best Used For |
| --- | --- | --- |
| string | string | Short text, taglines, labels |
| text | string | Long plain text, summaries, excerpts |
| number | number | Prices, ratings, floats |
| integer | number (whole) | Sort order, counts, version numbers |
| boolean | boolean | Flags, toggles, feature switches |
| datetime | string (ISO 8601) | Publish dates, event times |
| image | { id, src, alt, width, height } | Featured images, thumbnails, avatars |
| reference | string (ULID) | Parent relationships, related entries |
| select | string (one of options) | Status, category, type enums |
| portableText | PortableText block array | Rich body content, long-form articles |
| json | any (parsed JSON) | Unstructured configs, metadata blobs |

## Validation Options

Validation is declared inline on each field. EmDash enforces these rules both in the admin UI and at the API level, so you get consistent guarantees regardless of how content is created.

- required: true — field must have a value before publishing
- min / max — numeric range for number and integer fields; character count for string and text
- pattern — regex string applied to string fields (e.g. URL format, SKU format)
- options — array of allowed values for select fields (required for that type)

## Querying Fields in Astro Pages

After running `npx emdash types`, every field is typed on `entry.data`. Access is straightforward — no casting or null coalescing required for required fields.

## Gotchas Worth Knowing

Three things tripped me up early on. First: the `image` type returns an object, not a URL string. Render it with `<Image image={entry.data.featured_image} />` from `emdash/ui` — not a plain `<img>` tag.

Second: the `reference` type stores the ULID of the referenced entry — not the slug. Use `entry.data.id` (the database ULID) when passing values to reference-based APIs, not `entry.id` (the slug).

Third: `"version"` is a reserved field slug in EmDash v0.1. If you try to use it, your collection will misbehave silently. Use something like `release_version` instead.

:::pullquote
The field system is the contract between your schema and your templates. Define it once in seed.json, and the admin UI, type generation, and query layer all follow automatically.
:::

With your fields defined and types generated, you have everything you need to build strongly-typed content-driven pages. The next step is understanding how EmDash organizes content into collections — and how to design those collections for your specific site architecture.

[Read: EmDash Custom Content Types](/learn/emdash-custom-content-types)

