• CMSnpm version

    • Overview
    • ContentBuilder
    • InviteUserForm
    • LoginForm
    • RenewPasswordForm
  • UInpm version

    • Overview
    • Accordion
    • AlertBubble
    • AnnouncementBar
    • Avatar
    • AvatarFileInput
    • Badge
    • Button
    • ButtonGroup
    • ButtonList
    • Calendar
    • Checkbox
    • CheckboxButton
    • CheckboxInput
    • CheckboxList
    • Chip
    • ColorRadio
    • ColorRadioGroup
    • Combobox
    • DatePicker
    • DatePickerInput
    • DateRangePicker
    • DateRangePickerInput
    • DatetimePicker
    • DatetimePickerInput
    • Dialog
    • Dropdown
    • Dropzone
    • ErrorMessage
    • FileInput
    • FlashMessages
    • FormComponent
    • Icon
    • IconButton
    • ImageGallery
    • InfoBox
    • Input
    • Label
    • Layout
    • Lightbox
    • ListItem
    • Loader
    • Lozenge
    • Menu
    • Message
    • Modal
    • ✅ ModalDialog
    • ✅ ModalHeader
    • MultiCombobox
    • MultiSelect
    • Pagination
    • Paper
    • Popover
    • Radio
    • RadioGroup
    • RasterImage
    • Select
    • ✅ Tabs
    • TextInput
    • TextLink
    • Textarea
    • TimePicker
    • TimePickerInput
    • Toggle
    • Tooltip
    • Typography
  • Formnpm version

    • Overview
    • AvatarFileInput
    • CheckboxButton
    • CheckboxInput
    • CheckboxList
    • ColorRadioGroup
    • Combobox
    • DatePickerInput
    • DateRangePickerInput
    • DatetimePickerInput
    • Dropzone
    • FileInput
    • Form
    • FormRenderer
    • GpsInput
    • MoneyInput
    • MultiCombobox
    • MultiSelect
    • NumberInput
    • PasswordInput
    • RadioGroup
    • Select
    • TextInput
    • Textarea
    • TimePickerInput
    • Toggle
  • DataGridnpm version

    • Overview
    • DataGrid
    • DataGridCustomExample
    • ExportButton
    • FilterList
    • Filters
    • FiltersButton
    • FulltextInput
    • HiddenColumns
    • HiddenColumnsButton
    • Pagination
    • RowCounts
    • RowsPerPageSelect
    • SelectedRowsToolbar
    • TableV2
    • ToolbarControl
    • ToolbarCustoms
    • ToolbarTabs
  • Wysiwygnpm version

    • Overview
  • Resizernpm version

    • Overview
  • Routernpm version

    • Overview
  • Corenpm version

    • Overview
  • Core-Reactnpm version

    • Overview
  • Stylesnpm version

    • Overview
  • Localizenpm version

    • Overview
  • Analyticsnpm version

    • Overview
  • Datepickernpm version

    • Overview
  • Icons-generatornpm version

    • Overview
  • Smart-addressnpm version

    • Overview
  • E2Enpm version

    • Overview
  • E2E-Playwrightnpm version

    • Overview
View source on GitLab

FormRenderer

Builds a complete @uxf/form form from a schema (a list of field configs) plus a map of field-renderer components. Instead of hand-writing every field, you describe the fields as data and let FormRenderer render, submit, and validate them.

When to use

Use FormRenderer when the set of fields is data-driven — typically a schema generated from a backend entity — rather than a fixed, hand-authored layout. It renders each schema entry through a renderer chosen by the entry's type, wraps everything in a Form, and appends a submit button.

For a small, static form, compose Form and the individual field components directly; you get more control over layout and per-field props than the schema allows.

Usage

Grounded in form-renderer.stories.tsx. The renderer components (base-field, embedded, one-to-many) ship with the package but are not re-exported from the package entry — import them from their file paths (each is a default export):

import { FormRenderer, FormRendererFields, FormSchema } from "@uxf/form/form-renderer";
import baseField from "@uxf/form/form-renderer/field/base-field";
import embedded from "@uxf/form/form-renderer/field/embedded";
import oneToMany from "@uxf/form/form-renderer/field/one-to-many";
import { useForm } from "react-hook-form";

interface FormData {
    name: string;
    active: boolean;
}

const schema: FormSchema<FormData> = {
    fields: [
        {
            autocomplete: null,
            editable: true,
            fields: [],
            label: "Name",
            name: "name",
            options: null,
            readOnly: false,
            required: true,
            type: "string",
        },
        {
            autocomplete: null,
            editable: true,
            fields: [],
            label: "Active",
            name: "active",
            options: null,
            readOnly: false,
            required: false,
            type: "boolean",
        },
    ],
};

const fields: FormRendererFields<FormData> = {
    default: baseField,
    embedded,
    oneToMany,
};

export function Example() {
    const formApi = useForm<FormData>();

    return <FormRenderer fields={fields} formApi={formApi} id="example-form" onSubmit={console.log} schema={schema} />;
}

For each entry in schema.fields, FormRenderer picks the renderer as fields[field.type] ?? fields.default and renders it with the field config and formApi.control. So default handles every scalar type, while composite types (embedded, oneToMany) need their own keys matching the schema type string.

Props

FormRendererProps<FormData> extends Omit<FormProps<FormData>, "children">. Only the props below are actually applied — see Gotchas for the inherited props that are accepted but ignored.

Prop Type Default Description
schema FormSchema<FormData> — Required. The list of field configs to render.
fields FormRendererFields<FormData> — Required. Map of schema type → renderer component; must include a default.
formApi UseFormReturn<FormData> — Required. From react-hook-form's useForm.
id string — Required. Form id (passed to the inner Form).
onSubmit SubmitHandler<FormData> — Required. Submit handler.
isEditing boolean undefined When true, fields whose config has editable: false are rendered disabled.
SubmitButton FunctionComponent<{ formState: FormState<FormData> }> built-in Custom submit button; receives formApi.formState. Defaults to a @uxf/ui/button that is disabled while formState.isSubmitting and labelled from the uxf-form-form-renderer:submit-button translation.

Schema shape

FormSchema<FormData> is { fields: FieldSchema<FormData>[] }. Each FieldSchema<FormData>:

Key Type Description
name Path<FormData> Field path in the form values.
type string Selects the renderer (fields[type]) and, in base-field, the concrete input.
label string Visible field label.
required boolean Adds a required rule / indicator (unless the field is disabled).
readOnly boolean Renders the field disabled.
editable boolean When false and isEditing is true, the field is disabled.
autocomplete string | null Autocomplete endpoint key for relation types (manyToOne / manyToMany).
options Array<{ id: string | number; label: string }> | null Options for the enum type.
fields FieldSchema<FieldValues>[] Nested field configs for composite types (embedded, oneToMany).

Field renderers

FormRendererFields<FormData> is { [type: string]: FieldComponent<FormData>; default: FieldComponent<FormData> } — a map from schema type to a FieldComponent, with a mandatory default. FieldComponent<FormData> is FunctionComponent<FieldProps<FormData>>.

The package ships three ready-made renderers under form-renderer/field/:

base-field (use as default)

Maps fieldSchema.type to a concrete @uxf/form field. It derives state as isDisabled = readOnly || (isEditing && !editable) and isRequired = !isDisabled && required.

type Renders
"file", "logo", "image" FileInput
"files", "images" DropzoneInput + DropzoneList
"enum" Select (from fieldSchema.options)
"boolean" CheckboxInput
"date" DatePickerInput
"datetime" DatetimePickerInput
"time" TimePickerInput
"integer" NumberInput
"text" Textarea
"manyToMany" MultiCombobox (loads options via fieldSchema.autocomplete)
"manyToOne" Combobox (loads options via fieldSchema.autocomplete)
anything else (e.g. "string") TextInput

embedded (register under key embedded)

Renders the nested fieldSchema.fields as a group, prefixing each child field name with <name>. so it maps to a nested object in the form values.

one-to-many (register under key oneToMany)

Renders a repeatable list backed by react-hook-form's useFieldArray, with add/remove buttons (labels from the uxf-form-form-renderer:fields.one-to-many.* translations). Child field names are prefixed with <name>.<index>..

FieldProps<FormData>

The props every renderer receives:

Prop Type Notes
fieldSchema FieldSchema<FormData> The config for this field.
fields FormRendererFields<FormData> The full renderer map (so composite renderers can render children).
control Control<FormData> react-hook-form control.
isEditing boolean Passed through from FormRenderer.
prefix string Name prefix applied by composite renderers to nested fields.
overrides { autocomplete?; upload?; onRemoveConfirm? } Handler overrides for relation/upload fields. Not populated by FormRenderer — only useful when you render a field component yourself.

Requirements

  • CSS — import the renderer stylesheet from the published package:

    @import url("@uxf/form/css/form-renderer.css");
    

    It uses Tailwind @apply / theme(), so it must be processed by the consuming app's Tailwind build. It only styles the renderer's own layout (.uxf-form-renderer__body, .uxf-form-renderer__footer); you still need the stylesheets and the @uxf/ui token layer for whichever fields base-field renders.

  • Translations — the built-in submit button and the one-to-many renderer read the uxf-form-form-renderer translation namespace via @uxf/core-react/translations; register it in the app.

  • Client boundary — it renders Form (a client component) and uses hooks, so render it inside a client component.

Gotchas

  • FormRenderer only forwards formApi, id, and onSubmit to the inner Form, and always sets the form class to uxf-form-renderer. The following props are inherited from FormProps (or declared) but silently ignored: className, method, onError, isDisabled, isReadOnly, shouldOmitSubmitInput, forwardRef. Use a custom SubmitButton or a plain Form if you need those.
  • fields.default is required by the type — omitting it is a type error, and any schema type without a matching renderer key falls back to it.
  • mapToRequestObject (a helper that maps form values to a request payload per the schema) lives in this directory but is not exported from the package entry.
Example
Open in new tab