Skip to main content

Computed fields

A computed field holds a {{ }} template instead of a stored value. Its value is rendered read-only every time the record is read, so it reaches every path the same way: list rows, detail pages, search, export, and the public surface. Nothing is saved; the value is derived fresh from the record each time.

Add one like any other field, choosing the computed type (see Fields and field types) and giving it a template.

Two tiers, one template

A template can reference two kinds of data.

Tier 1 is the record's own fields, both native and custom:

{{year}} {{manufacturer}} {{model}} → "2022 Toyota Corolla"
{{qty}} {{unit}} → "340 g"

Tier 2 is related data a module exposes under a namespace. A module can register a context provider, so a computed field can pull in a value the record does not store itself:

{{maintenance.last_performed}} ({{maintenance.last_performed_at | relative}})
→ "Oil change (2 weeks ago)"

A tier-2 provider is only invoked when a template on that kind actually references its namespace, so a field that never mentions maintenance costs nothing, and the computed layer never depends on any specific module.

Filters

A {{ }} reference can carry a filter:

  • {{ value | relative }} formats a date or timestamp as a coarse phrase ("2 weeks ago", "yesterday").
  • {{ value | slug }} makes a URL-safe slug, for building a link.
  • {{ value | default: "fallback" }} substitutes when the value is empty or missing.

Wires use the same templates in their messages and arguments, with one difference: | relative is computed-fields only, so a wire renders the raw value instead. See wires.

What it is good for

A computed field is the place for a value that is always a function of other values: a full display name assembled from parts, a quantity with its unit, a "days since last serviced" line, a link built from an id. Because it renders on every read, it never goes stale, and because it is read-only, there is no second copy to keep in sync.