Skip to content
Playground

Theme and resolver

Essentially theme is a simple function:

const fromRecord = (record) => (type) => record[type]
const theme = fromRecord(components)

All components can be divided into four logical types:

  • components
    • form
    • submitButton
    • button
    • layout
    • title
    • label
    • description
    • help
    • errorsList
  • widgets
    • textWidget
    • numberWidget
    • selectWidget
    • checkboxWidget
  • templates
    • fieldTemplate
    • objectTemplate
    • objectPropertyTemplate
    • arrayTemplate
    • arrayItemTemplate
    • multiFieldTemplate
  • fields
    • stringField
    • numberField
    • integerField
    • booleanField
    • objectField
    • arrayField
    • tupleField
    • nullField
    • oneOfField
    • anyOfField

Also found out that the above components are enough to display any JSON schema.

To determine which field to use to display your JSON schema, the form uses resolver.

interface Config {
id: Id;
name: string;
title: string;
schema: Schema;
uiSchema: UiSchema;
uiOptions: UiOptions | undefined;
required: boolean;
}
type ResolveFieldType = (config: Config) => FoundationalFieldType
type Resolver = (ctx: FormInternalContext<Validator>) => ResolveFieldType

It looks complicated, but let’s take a look at the basic resolver implementation (@sjsf/form/resolvers/basic):

function resolver(): ResolveFieldType {
return ({ schema }) => {
if (schema.oneOf !== undefined) {
return "oneOfField";
}
if (schema.anyOf !== undefined) {
return "anyOfField";
}
const type = getSimpleSchemaType(schema);
if (type === "array") {
return isFixedItems(schema) ? "tupleField" : "arrayField";
}
return `${type}Field`;
}
}

Yes, it’s essentially a direct mapping of schema type to field type, nothing fancy. Let’s talk about FoundationalFieldType.

The FoundationalFieldType type is a subset of the fields from the FoundationalComponentType type. In turn, FoundationalComponentType is a subset of all components (ComponentType) that can be explicitly used in form elements.

For example, this is possible because form is a FoundationalComponentType

<script lang="ts">
...
const Form = $derived(getComponent(ctx, "form", config));
</script>
<Form bind:ref {config} {children} {attributes} />

The main purpose of this list is to determine which components you can replace using the ui:components property from UiSchema.

This is an extensible list, but by default it corresponds to the components listed in Component types.

If the default set of components is insufficient, you can add the necessary components yourself.

The sjsf library provides definitions and implementation of several extra fields, as well as a set of definitions for extra widgets.

Here is a list of extra fields that can be imported from @sjsf/form/fields/extra-fields/*.

  • boolean-select
  • enum
  • file
  • files
  • multi-enum
  • tags

To use them you can import them directly

import EnumField from "@sjsf/form/fields/extra-fields/enum";

or use an include import

import "@sjsf/form/fields/extra-fields/enum-include";

and replace the compatible field with it in uiSchema.

const uiSchema: UISchema = {
"ui:components": {
stringField: EnumField,
// Or if you used the `include` import
stringField: "enumField"
}
}

There are several types of extra widgets already defined in the library (@sjsf/form/fields/extra-widgets/*):

  • checkboxes
  • date-picker
  • file
  • multi-select
  • radio-buttons
  • radio (group)
  • range
  • rating
  • switch
  • tags
  • textarea

However, the ability to use them depends on the availability of a corresponding implementation in your chosen theme.