Wrappers decorate a rendered field with extra UI chrome — a titled section, a validation indicator, a card, a collapsible panel — without modifying the field component itself. Multiple wrappers stack outermost → innermost.
How a wrapper chain looks
With wrappers: [{ type: 'section' }, { type: 'css' }] on a field, the outlet plugs each wrapper into the previous one's #fieldComponent slot — outermost first, field component last:
Quick shape
{
key: 'email',
type: 'input',
label: 'Email',
wrappers: [{ type: 'section', title: 'Contact details' }],
}Live example
The form below layers a custom section wrapper on one field, inherits a form-level default wrapper on another, and opts the last field out entirely with wrappers: null.
The horizontal layout you see everywhere else in these docs is also a wrapper in action — { type: 'row', fields: [...] } resolves to the container field with an auto-injected row wrapper. You never write the wrapper explicitly; it's an implementation detail of the row type.
Built-in wrappers
Two wrappers ship with the library and are registered automatically:
css— adds space-separated classes to a wrapping element. Accepts aDynamicTextforcssClasses(string,Signal, orObservable):{ type: 'css', cssClasses: 'card p-4' }row— the flex/grid layout used under the hood when you write{ type: 'row', fields: [...] }. Keep writingtype: 'row'— the wrapper plumbing is hidden.
When to reach for a wrapper
Wrappers are one of three extension points. Pick the smallest that solves the problem.
| Problem | Use | Cost |
|---|---|---|
| A static CSS class on the field's existing host | className on the field |
Free — no wrapper instantiated |
| Consistent chrome (card, section header, badge) around many fields | Wrapper | One component per wrapper level |
| A brand-new control (rich-text editor, file picker, colour picker, …) | Custom field | New component + registration |
| Same chrome on every field in a form | FormConfig.defaultWrappers |
One line |
Auto-decorate all fields of a specific type (e.g. every input) |
Wrapper with types: ['input'] auto-association |
One line in registration |
Wrappers are read-only — they observe field state (value, validity, errors) but never mutate. Mutation belongs in the field component.
SSR
Wrappers are SSR-safe. The component cache (WRAPPER_COMPONENT_CACHE) and registry (WRAPPER_REGISTRY) are DI-scoped rather than module-scoped, so there's no shared state between server renders.
Next
- Writing a wrapper — build your own wrapper component: the contract, reading field state, styling, testing.
- Registering and applying wrappers —
createWrappers(…), module augmentation,wrapperson a field,defaultWrappers, auto-associations, opting out.