Skip to content
On this pageDefining Fields in seed.json
  1. Defining Fields in seed.json
  2. All 11 Field Types at a Glance
  3. Validation Options
  4. Querying Fields in Astro Pages
  5. Gotchas Worth Knowing

EmDash Custom Fields: A Practical Reference

Ben 3 min read

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.

{
  "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.

TypeRuntime ShapeBest Used For
stringstringShort text, taglines, labels
textstringLong plain text, summaries, excerpts
numbernumberPrices, ratings, floats
integernumber (whole)Sort order, counts, version numbers
booleanbooleanFlags, toggles, feature switches
datetimestring (ISO 8601)Publish dates, event times
image{ id, src, alt, width, height }Featured images, thumbnails, avatars
referencestring (ULID)Parent relationships, related entries
selectstring (one of options)Status, category, type enums
portableTextPortableText block arrayRich body content, long-form articles
jsonany (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.

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