Skip to main content

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

PropertyTypeDescription
contentstring | undefinedStatic text content from the field definition
sourceValueunknown | undefinedResolved value from a computed field referenced by source
formatstring | undefinedFormat hint for displaying numeric values
variantstring | undefinedPresentation variant (inherited from BaseFieldProps)
variantConfigRecord<string, unknown>Variant-specific configuration (inherited from BaseFieldProps)
valueneverNot available on display fields
onChangeneverNot available on display fields
labelstringDisplay label (falls back to field path if not set)
descriptionstring | undefinedHelp text
visiblebooleanWhether the field is visible
namestringField 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:

VariantPurposevariantConfig Keys
textBody text or paragraphs (default)-
headingSection headingslevel (2-5)
dividerVisual separator between sections-
metricLarge numeric display with optional unitunit (e.g., "kg", "USD")
alertContextual alert with severityseverity ("info", "warning", "error", "success")
calloutInformational callout with optional iconicon (e.g., "info", "lightbulb", "shield")
summaryCard-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>
);
}