Display Fields
Display fields show content without collecting user input. Use them for headings, dividers, informational text, computed metrics, alerts, and other read-only presentation content within a form.
ComponentMap Setup
Add the display key to your component map:
import type { ComponentMap } from "@fogpipe/forma-react";
const components: ComponentMap = {
text: TextInput,
number: NumberInput,
// ... other field types
display: DisplayField,
};
Component Props
Display field components receive DisplayComponentProps:
import type { DisplayComponentProps } from "@fogpipe/forma-react";
function DisplayField({ field }: DisplayComponentProps) {
// field.content — static text from the field definition
// field.sourceValue — resolved computed value (from `source` property)
// field.format — format hint (e.g., "currency", "percent", "decimal(2)")
// field.variant — presentation variant (e.g., "heading", "divider", "metric")
// field.variantConfig — variant-specific options (e.g., { level: 2 }, { severity: "warning" })
}
DisplayFieldProps
| Property | Type | Description |
|---|---|---|
content | string | undefined | Static text content from the field definition |
sourceValue | unknown | undefined | Resolved value from a computed field referenced by source |
format | string | undefined | Format hint for displaying numeric values |
variant | string | undefined | Presentation variant (inherited from BaseFieldProps) |
variantConfig | Record<string, unknown> | Variant-specific configuration (inherited from BaseFieldProps) |
value | never | Not available on display fields |
onChange | never | Not available on display fields |
label | string | Display label (falls back to field path if not set) |
description | string | undefined | Help text |
visible | boolean | Whether the field is visible |
name | string | Field path/identifier |
Static Display
Display fields with content render static text defined in the Forma spec:
{
"id": "welcome_message",
"type": "display",
"label": "Welcome",
"content": "Please fill out this form to complete your registration.",
"variant": "text"
}
function StaticDisplay({ field }: DisplayComponentProps) {
if (!field.visible) return null;
return (
<div>
{field.label && <p className="font-medium">{field.label}</p>}
{field.content && <p className="text-muted">{field.content}</p>}
</div>
);
}
Computed Display
Display fields can reference computed values using the source property in the Forma spec. The library resolves the referenced value and passes it as field.sourceValue:
{
"fields": {
"quantity": { "type": "number", "label": "Quantity" },
"price": { "type": "number", "label": "Unit Price" },
"total_display": {
"type": "display",
"label": "Total",
"source": "total",
"format": "currency",
"variant": "metric"
}
},
"computed": [{ "id": "total", "expression": "quantity * price" }]
}
The sourceValue is resolved from form data and computed values:
function ComputedDisplay({ field }: DisplayComponentProps) {
if (!field.visible) return null;
const displayValue =
field.sourceValue != null ? String(field.sourceValue) : "\u2014";
return (
<div>
{field.label && <p className="font-medium text-muted">{field.label}</p>}
<p className="text-2xl font-semibold">{displayValue}</p>
</div>
);
}
Variant Routing
Use field.variant to render different presentations. Common variants include:
| Variant | Purpose | variantConfig Keys |
|---|---|---|
text | Body text or paragraphs (default) | - |
heading | Section headings | level (2-5) |
divider | Visual separator between sections | - |
metric | Large numeric display with optional unit | unit (e.g., "kg", "USD") |
alert | Contextual alert with severity | severity ("info", "warning", "error", "success") |
callout | Informational callout with optional icon | icon (e.g., "info", "lightbulb", "shield") |
summary | Card-based summary display | - |
Complete Example
A full display field component with variant routing:
import type { DisplayComponentProps } from "@fogpipe/forma-react";
function DisplayField({ field }: DisplayComponentProps) {
if (!field.visible) return null;
const variant = field.variant ?? "text";
switch (variant) {
case "heading":
return <HeadingDisplay field={field} />;
case "divider":
return <hr />;
case "metric":
return <MetricDisplay field={field} />;
case "alert":
return <AlertDisplay field={field} />;
case "callout":
return <CalloutDisplay field={field} />;
case "text":
default:
return <TextDisplay field={field} />;
}
}
function TextDisplay({ field }: { field: DisplayComponentProps["field"] }) {
const body =
field.sourceValue != null ? String(field.sourceValue) : field.content;
return (
<div>
{field.label && <p className="font-medium text-muted">{field.label}</p>}
{body && <p>{body}</p>}
</div>
);
}
function HeadingDisplay({ field }: { field: DisplayComponentProps["field"] }) {
const level = (field.variantConfig?.level as number) ?? 3;
const text = field.content ?? field.label ?? "";
const Tag = `h${Math.min(Math.max(level, 2), 5)}` as
| "h2"
| "h3"
| "h4"
| "h5";
return <Tag>{text}</Tag>;
}
function MetricDisplay({ field }: { field: DisplayComponentProps["field"] }) {
const value =
field.sourceValue != null ? String(field.sourceValue) : "\u2014";
const unit = field.variantConfig?.unit as string | undefined;
return (
<div>
{field.label && <p className="text-sm text-muted">{field.label}</p>}
<p className="text-2xl font-semibold">
{value}
{unit && <span className="text-base text-muted ml-1">{unit}</span>}
</p>
</div>
);
}
function AlertDisplay({ field }: { field: DisplayComponentProps["field"] }) {
const severity = (field.variantConfig?.severity as string) ?? "info";
const body =
field.sourceValue != null ? String(field.sourceValue) : field.content;
return (
<div className={`alert alert-${severity}`} role="alert">
{field.label && <strong>{field.label}</strong>}
{body && <p>{body}</p>}
</div>
);
}
function CalloutDisplay({ field }: { field: DisplayComponentProps["field"] }) {
const body =
field.sourceValue != null ? String(field.sourceValue) : field.content;
return (
<div className="callout">
{field.label && <p className="font-medium">{field.label}</p>}
{body && <p className="text-muted">{body}</p>}
</div>
);
}