UI Schema
UI schema allows you to customize the appearance of the form and influence its behavior.
The UI schema object follows the tree structure of the form field hierarchy, and defines how each property should be rendered.
export interface UiSchemaContent { /** * Extendable set of UI options */ "ui:options"?: ResolvableUiOptions; /** * Components override */ "ui:components"?: Partial<{ [T in FoundationalComponentType]: | Exclude<CompatibleComponentType<T>, T> | ComponentDefinitions[T]; }>; items?: UiSchemaDefinition | UiSchemaDefinition[]; anyOf?: UiSchemaDefinition[]; oneOf?: UiSchemaDefinition[]; combinationFieldOptionSelector?: UiSchemaDefinition; additionalProperties?: UiSchemaDefinition; additionalPropertyKeyInput?: UiSchemaDefinition; additionalItems?: UiSchemaDefinition;}
export type UiSchema = UiSchemaContent & { // This is should be `UiSchemaDefinition` type, but // https://github.com/microsoft/TypeScript/issues/17867 [key: string]: UiSchemaContent[keyof UiSchemaContent];};
export interface UiSchemaRef { $ref: string;}
export type UiSchemaDefinition = UiSchema | UiSchemaRef;
export type UiSchemaRoot = UiSchemaDefinition & { "ui:globalOptions"?: UiOptions; "ui:definitions"?: Record<string, UiSchema>;};Evaluation rules
Section titled “Evaluation rules”Usually UI schema corresponds to the data structure described by json schema.
For example, with this JSON schema, the following UI schema would be correct:
import type { Schema, UiSchemaRoot } from "@sjsf/form";
const schema: Schema = { oneOf: [ { properties: { foo: { type: "string", }, }, }, { properties: { bar: { type: "number", }, }, }, ],};
const uiSchema: UiSchemaRoot = { foo: { // ui schema for `foo` field }, bar: { // ui schema for `bar` field },};Special cases:
If the UI Schema contains a $ref key with a value of type string,
the ui:definitions field of the root UI schema will be searched
for the value of the $ref key, other fields will be ignored.
{ "ui:definitions": { "foo": { ... } }, properties: { foo: { $ref: "foo" } }}Instead of defining indices in the UI schema, the items keyword should be used
to specify the UI schema for the elements of the array.
For a fixed array items also can be an array.
If you have additional items you should use additionalItems keyword
to specify the UI schema for them.
{ items: [<uiSchema>, ...], additionalItems: <uiSchema>}Object
Section titled “Object”You should use additionalProperties keyword to specify the UI schema for
additional properties.
You can use additionalPropertyKeyInput keyword to define an UI schema for
the additional property key input field.
oneOf/anyOf
Section titled “oneOf/anyOf”You can define separate UI schemas for each oneOf/anyOf branch
using the corresponding keyword in the UI schema.
Otherwise the UI schema of the current field will be used.
{ oneOf: [<uiSchema>, ...]}UI components
Section titled “UI components”Using the ui:components property, you can replace any
form component with a compatible one
using the name of the connected component or
the component itself directly.
Component A is compatible with component B if the properties and bindings
of component B extend the properties and bindings of component A.
export type CompatibleComponentType<T extends ComponentType> = { [C in ComponentType]: Expand<ComponentProps[T]> extends Expand< ComponentProps[C] > ? ComponentBindings[T] extends ComponentBindings[C] ? C : never : never;}[ComponentType];UI options
Section titled “UI options”The UiOptions type is an extensible set of components options. By default it
looks like this:
type ItemTitle = ( title: string, index: number, fixedItemsCount: number, itemValue?: SchemaValue | undefined) => string;
type AdditionalPropertyKey = (key: string, attempt: number) => string;
interface UiOptions { /** * Overrides the title of the field. */ title?: string; /** * Overrides the description of the field (over the widget). */ description?: string; /** * List of labels for enum values in the schema. */ enumNames?: string[]; /** * List of enum values that are disabled. Values are compared by strict equality. */ disabledEnumValues?: SchemaValue[]; /** * Order of properties in the object schema. * You must specify all properties or use the wildcard `*`. */ order?: string[]; /** * Allow adding new properties to the object schema with `additionalProperties`. * @default true */ expandable?: boolean; /** * Allow adding new items to the array schema. * @default true */ addable?: boolean; /** * Allow reordering items in the array schema. * If you want an orderable array of file fields, set this to `true` explicitly. * @default true */ orderable?: boolean; /** * Allow removing items from the array schema. * @default true */ removable?: boolean; /** * Allow duplicating items in the array schema. * @default false */ copyable?: boolean; /** * Overrides the logic for creating a title for array elements */ itemTitle?: ItemTitle; /** * Overrides the logic for creating a new key for an additional property */ additionalPropertyKey?: AdditionalPropertyKey; /** * Help text for the field (under the widget). */ help?: string; /** * Hide the title of the field. * If you want to show a title of the `boolean` field this should be set to `false` explicitly. * @default false */ hideTitle?: boolean; /** * Overrides whether to use the `title` or `label` component in the `field` template */ useLabel?: boolean; /** * Display errors from child elements (applies only to aggregating fields like `tags`). * @default false */ collectErrors?: boolean; /** * Overrides form translation */ translations?: Partial<TranslatorDefinitions>; /** * Field action */ action?: FieldAction<SchemaValue | undefined>; /** * A typed field action, takes precedence over `action` */ actions?: Partial<{ [T in ActionField]: FieldAction<ComponentProps[T]["value"]>; }>;}Registry
Section titled “Registry”You can use UI options registry to ensure serializability of the UI schema.
export interface UiOptionsRegistry {}
export type ResolvableUiOption<T> = | { [K in keyof UiOptionsRegistry as UiOptionsRegistry[K] extends T ? K : never]: `registry:${K}`; }[keyof UiOptionsRegistry] | T;
export type ResolvableUiOptions = { [K in keyof UiOptions]: ResolvableUiOption<UiOptions[K]>;};To do this, you need to extend the UiOptionsRegistry interface and replace
the non-serializable value with registry:${key} in UiOptions.
Example:
import { type ItemTitle, createForm } from "@sjsf/form"
declare module "@sjsf/form" { interface UiOptionsRegistry { myItemTitle: ItemTitle }}
const form = createForm({ ..., uiSchema: { "ui:options": { itemTitle: "registry:myItemTitle" } }, uiOptionsRegistry: { myItemTitle: (title, index) => `${title} [${index}]` }})Conventions
Section titled “Conventions”- Each
component/widgetin the theme should define at least one UI option to allow customization of it - All parameters must be prefixed by the theme name (e.g.
daisyui5RadioButtons). - Only a
basictheme can define options without a prefix and other themes should use thebasictheme UI options for the correspondingcomponents/widgetsif their properties are compatible. - Using UI options of one component in another (even if they are compatible)
is forbidden, e.g.
textandtextareawidgets must use separate options.
Check your theme page for an extended list of UI options:
Extra UI options
Section titled “Extra UI options”This property allows you to specify UI options for all components of a certain type or set them dynamically.
import { fromRecord } from "@sjsf/form/lib/resolver";import { createForm } from '@sjsf/form';
import * as defaults from "@/lib/form/defaults";
const form = createForm({ ...defaults, schema: {}, extraUiOptions: fromRecord({ titleAttributes: { style: "font-weight: bolder;", }, }),});