Specification
This package defines the theme contract for Formbox Renderer. Use it when you build a theme or want strict typing while customizing an existing theme.
Install
If you only need types in an app, install as a dev dependency:
pnpm add -D @formbox/theme
If you are publishing a theme package, add it as a dependency:
pnpm add @formbox/theme
Quick start
Create a theme by implementing the Theme contract. The easiest path is to start from an existing theme and override the components you want to replace.
import type { Theme } from "@formbox/theme";
import Renderer from "@formbox/renderer";
import { theme as baseTheme } from "@formbox/hs-theme";
const theme: Theme = {
...baseTheme,
Label: MyLabel,
};
<Renderer fhirVersion="r5" questionnaire={questionnaire} theme={theme} />;
Custom extensions
Use theme.customExtensions to declare FHIR extension definitions by key. Each definition declares target ("questionnaire" or "item"), url, repeats, and an extract callback.
When repeats is true, extract runs per matching extension and the resolved value is an array of extracted values.
Resolved values are consumed in theme components with useCustomExtension(key) from @formbox/theme.
import {
type InferCustomExtensionValues,
useCustomExtension,
} from "@formbox/theme";
const customExtensions = {
formShared: {
target: "questionnaire",
url: "http://example.org/fhir/StructureDefinition/form-shared",
repeats: false,
extract: (extension: unknown) => extension.valueString,
},
} as const;
declare module "@formbox/theme" {
interface CustomExtensionValueRegistry extends InferCustomExtensionValues<
typeof customExtensions
> {}
}
const formShared = useCustomExtension("formShared");
Theme contract
A Theme is a full object with React components for every slot listed in the reference. The renderer never touches DOM APIs directly; the theme owns markup, layout, and styling. Data flows only through props.
The Theme type is strict. You must supply every component, either by building a complete theme from scratch or by extending a base theme.
Conventions
- Inputs are controlled. Callbacks receive values, not DOM events.
- Use
disabledto indicate non-editable state; avoidreadOnlyunless your component needs it. ariaLabelledByandariaDescribedByare id strings. Forward them directly to the focusable element.- When
idis provided, apply it to the primary focusable element. childrenis the slot for rendered content. Option types uselabelfor display content.useStrings()returns translation strings for the currently selected language. Use those strings instead of hardcoding text to make your themes multilingual.- If present, render
Form.languageSelectorin your form layout to expose questionnaire language switching. - When a prop is optional, the renderer may omit it. Treat
undefinedas not provided.
Renderer composition overview
The renderer composes your theme in a predictable tree. You control layout, but the nesting describes where headers, errors, and actions appear.
Overview diagram (simplified):
Group list composition for repeating groups:
Typical question node:
QuestionScaffold
Label
AnswerList (or a single control)
AnswerScaffold (per answer)
control (TextInput/Select/etc.)
remove action (when onRemove is provided)
children (nested nodes)
errors (Errors)
Errors (question-level)
Typical repeating group list:
GroupList
Label (only when list has text)
GroupScaffold (per instance)
remove action (when onRemove is provided)
errors
add action (when onAdd is provided)
Typical non-repeating group:
GroupScaffold
Label (when visible)
Stack (child nodes)
Errors
Renderer guarantees
id,ariaLabelledBy, andariaDescribedByvalues are unique within a render and stable for a given node or answer.ariaDescribedBystrings are already space-joined; use them as-is.- Option tokens are stable across renders; selected options may remain when the options list changes.
- When needed, the renderer passes disabled legacy options so stored answers still render.
labelandchildrenprops are ready-to-renderReactNodevalues.
Next
See reference.md for component props and behavior.md for runtime behavior details.
