React JSON Schema Form (RJSF)

repository·main·Indexed 12 days ago

https://github.com/rjsf-team/react-jsonschema-form

A React-based library for the declarative creation of web forms using JSON Schema. It is highly extensible via pluggable validators and theme-specific packages, including support for Ant Design (@rjsf/antd), Chakra UI (@rjsf/chakra-ui), DaisyUI (@rjsf/daisyui), Fluent UI (@rjsf/fluentui-rc), Mantine (@rjsf/mantine), and a core Bootstrap 3 theme (@rjsf/core).

Tokens
176.4K
Snippets
681
Records
807
Agent score
93%

What's inside React JSON Schema Form

  1. Overview of react-jsonschema-form

    main
    react-jsonschema-form is a React component that uses JSON Schema to declaratively build and customize web forms. It allows developers to define form structures using standard JSON Schema and automatically generates the corresponding UI components.
  2. Understand the DaisyUI testing strategy

    main

    The DaisyUI theme testing strategy is divided into three main layers to ensure functional correctness, UI fidelity, and accessibility:

    1. Core Snapshot Tests: Validates the overall form rendering, array fields, and object fields using the shared RJSF snapshot infrastructure.
    2. DaisyUI-Specific Component Tests: Unit tests for components unique to this theme, such as:
      • DaisyUIFrameProvider: Verifies iframe theme injection.
      • ToggleWidget: Tests the toggle component interaction.
      • RatingWidget: Tests the rating component interaction.
      • ArrayFieldItemTemplate: Tests the card-based UI used for array items.
    3. Helper Functions: Uses createMocks.ts to generate mock props for form components.

    Coverage Areas include:

    • Functional: Widget interaction (toggles, ratings), form validation, and data binding.
    • UI: Application of DaisyUI CSS classes, responsive layouts, and theme application.
    • Accessibility: Keyboard navigation, ARIA attributes, and focus management.
    • Theme Management: Theme switching and persistence in localStorage.
  3. Use @rjsf/validator-ata as an alternative to AJV

    main

    The @rjsf/validator-ata package is an alternative validation engine for React JSON Schema Form, powered by ata-validator instead of AJV.

    Because its public API mirrors @rjsf/validator-ajv8, you can often swap the validator by simply changing your imports. It supports the same core customization patterns including customizeValidator(), ValidatorType, custom formats, transformErrors, customValidate, and suppressDuplicateFiltering.

    Key Differences from AJV:

    • Options: Instead of ajvOptionsOverrides, use ataOptionsOverrides to pass options directly to ata-validator (e.g., coerceTypes, removeAdditional, verbose, abortEarly).
    • Formats: The ata-validator format set is always installed; there is no opt-in flag.
    • Error Handling: ata-validator error parameters are frozen, so a pre-quote pass for i18n is not required, though localizers that mutate the message property still work.
  4. What is a uiSchema?

    main

    While JSON Schema defines what data the form should contain, the uiSchema defines how that data should be rendered. It is an object literal that follows the tree structure of the form field hierarchy, providing instructions for UI components (widgets, fields, styles, etc.).

    Most properties in a uiSchema can be defined in two equivalent ways:

    1. Using a direct ui:[property] key.
    2. Using a nested ui:options object.

    Example of equivalence:

    // Option 1: Direct
    {
      "ui:title": "Title",
      "ui:classNames": "my-class"
    }
    
    // Option 2: Nested
    {
      "ui:options": {
        "title": "Title",
        "classNames": "my-class"
      }
    }
    {
      "ui:title": "Title",
      "ui:description": "Description",
      "ui:classNames": "my-class",
      "ui:submitButtonOptions": {
        "props": {
          "disabled": false,
          "className": "btn btn-info"
        },
        "norender": false,
        "submitText": "Submit"
      }
    }
  5. Configure ui:row for grid layouts

    main

    The ui:row is the outermost level of a LayoutGridField. It defines nested rows, columns, or conditional elements. It can be defined in two ways:

    1. Simple definition: An array of "grid elements" (e.g., ui:col, ui:row, ui:columns, or ui:condition).
    2. Complex definition: An object containing native GridTemplate implementation-specific props (like spacing, size, className, etc.) and a children array of grid elements.

    Note on className: All className values are automatically looked up in the formContext.lookupMap if they are defined using a CSS-in-JS approach. If multiple classes are provided (e.g., 'GridRow GridColumn'), they are split, looked up individually, and rejoined.

    {
      "ui:row": [
        { "ui:row"|"ui:col"|"ui:columns"|"ui:condition": ... },
        ...
      ]
    }
    
    // Complex example with MUI Grid2 props
    {
      "ui:row": {
        "spacing": 2,
        "size": {
          "md": 4
        },
        "alignContent": "flex-start",
        "className": "GridRow",
        "children": [
            { "ui:row"|"ui:col"|"ui:columns"|"ui:condition": ... }
        ]
      }
    }
  6. Use @rjsf/validator-cfworker for CSP-constrained applications

    main

    Use @rjsf/validator-cfworker when your application is subject to strict Content Security Policy (CSP) constraints that forbid the use of eval or new Function.

    Unlike the default @rjsf/validator-ajv8, this validator is backed by @cfworker/json-schema and interprets schemas without using prohibited JavaScript execution patterns. It defaults to JSON Schema draft 2020-12.

    Important Limitations:

    • It is not a direct replacement for AJV. It does not support AJV-specific options or extensions like AjvClass, $data, discriminator, or ajv-errors's errorMessage.
    • $dynamicRef and $dynamicAnchor are not supported.
    • Error messages differ from AJV, and there is no ajv-i18n equivalent. To customize error messages, use the transformErrors prop in RJSF.
    • Precompiled-validator mode is not supported.
  7. Understand the difference between Custom Fields, Custom Templates, and Custom Widgets

    main

    When customizing React JSON Schema Form (RJSF), you can choose between three levels of abstraction depending on how much behavior you want to override:

    1. Custom Field: Overrides all behavior (layout, labels, help, validation, and the input itself). Can be applied globally or per-field.
    2. Custom Template: Overrides just the layout (e.g., how an array of items is wrapped or how a field is structured), but does not change the underlying behavior or input logic. Can be applied globally or per-field.
    3. Custom Widget: Overrides just the input box (the actual interactive element), but does not change the layout, labels, help text, or validation. Can be applied globally or per-field.

    Usage Patterns

    Global Application (via the Form component):

    • Fields: <Form fields={{ MyCustomField }} />
    • Templates: <Form templates={{ ArrayFieldTemplate: MyArrayTemplate }} />
    • Widgets: <Form widgets={{ MyCustomWidget }} />

    Per-Field Application (via the uiSchema):

    • Fields: "ui:field": MyCustomField
    • Templates: "ui:ArrayFieldTemplate": MyArrayTemplate
    • Widgets: "ui:widget": MyCustomWidget
    // Global Example
    <Form 
      schema={schema} 
      uiSchema={uiSchema} 
      templates={{ ArrayFieldTemplate: MyArrayTemplate }} 
      widgets={{ MyCustomWidget }}
      fields={{ MyCustomField }}
    />
    
    // Per-Field Example (uiSchema)
    const uiSchema = {
      myField: {
        "ui:field": MyCustomField,
        "ui:ArrayFieldTemplate": MyArrayTemplate,
        "ui:widget": MyCustomWidget
      }
    };
  8. Understand the difference between slotProps and rjsfSlotProps in @rjsf/mui

    main

    @rjsf/mui uses two distinct keys for slot customization to prevent 'prop bleeding' (accidentally passing configuration to unintended child components):

    1. slotProps: Used for standard MUI customization. These are passed directly to MUI's native slotProps API on a component (e.g., targeting htmlInput, input, or inputLabel on a TextField).
    2. rjsfSlotProps: Used specifically for RJSF template components (like ArrayFieldTemplate or ObjectFieldTemplate). This key targets RJSF-specific sub-components (like paper, grid, or box) and is explicitly extracted by the library.
    /* Example of slotProps for a MUI widget */
    {
      "myPriceField": {
        "ui:options": {
          "mui": {
            "slotProps": {
              "input": {
                "startAdornment": "$"
              }
            }
          }
        }
      }
    }
    
    /* Example of rjsfSlotProps for a structural template */
    {
      "myArrayField": {
        "ui:options": {
          "mui": {
            "rjsfSlotProps": {
              "arrayPaper": {
                "elevation": 10
              }
            }
          }
        }
      }
    }
  9. Configure ui:col for columns

    main

    The ui:col element specifies columns within a ui:row. It supports several formats:

    1. Simple list: An array of dotted-path field names (e.g., ["field1", "field2.subfield"]).
    2. Object list: An array of objects containing a name (dotted-path) and other props that are gathered into ui:options.
    3. Custom render: An array containing a one-off functional component. If a name is provided, it maps to a field; otherwise, it's treated as a custom component. If render is a string, it is looked up in formContext.lookupMap.
    4. Complex object: An object with native GridTemplate props and a children array containing any of the above types.

    Fallback behavior: If a name does not match a schema field, it is assumed to be a custom render component. If render is missing, it results in a null render.

    {
      "ui:col": ["innerField", "inner.grandChild"]
    }
    
    {
      "ui:col": [
        { "name": "innerField", "fullWidth": true },
        { "name": "inner.grandChild", "fullWidth": false }
      ]
    }
    
    {
      "ui:col": [
        "innerField",
        {
          "render": "WizardNavButton",
          "isNext": true,
          "size": "large"
        }
      ]
    }
    
    {
      "ui:col": {
        "size": { "md": 4 },
        "className": "GridColumn",
        "children": [
          "innerField",
          { "name": "inner.grandChild", "fullWidth": true },
          { "name": "customRender", "render": "CustomRender", "toSpread": "prop-value" },
          { "ui:row|ui:condition": ... }
        ]
      }
    }
  10. Use allOf in JSON Schema

    main

    A schema using allOf is valid only if all of the provided subschemas are valid.

    Internally, react-jsonschema-form uses the @x0k/json-schema-merge library to merge the specified subschemas into a single combined subschema. For example, if one subschema requires type: ['string', 'boolean'] and another requires type: 'boolean', the resulting merged schema will effectively require type: 'boolean'.

    import { RJSFSchema } from '@rjsf/utils';
    import validator from '@rjsf/validator-ajv8';
    
    const schema: RJSFSchema = {
      title: 'Field',
      allOf: [
        {
          type: ['string', 'boolean'],
        },
        {
          type: 'boolean',
        },
      ],
    };
    
    render(<Form schema={schema} validator={validator} />, document.getElementById('app'));
  11. Use ui:columns as syntactic sugar for ui:col

    main

    The ui:columns element is a shorthand for defining multiple ui:col elements that share the same native GridTemplate props. Instead of repeating the same props for every column, you can wrap a children array in a single ui:columns block.

    Difference from ui:col: Using ui:columns with a children array renders all those children inside a single <GridTemplate> element with the specified props, whereas multiple ui:col elements would each render their own <GridTemplate> element.

    {
      "ui:row": {
        "children": [
          {
            "ui:columns": {
              "className": "GridColumn col-md-4",
              "children": ["innerField", "inner.grandChild", { "name": "inner.grandChild2", "fullWidth": true }]
            }
          },
          {
            "ui:columns": {
              "className": "col-md-6",
              "children": ["innerField2", "inner.grandChild3"]
            }
          }
        ]
      }
    }
  12. Handle changes in primitive field and array defaulting

    main

    Two bug fixes in v6 change the behavior of data initialization, which may impact applications relying on specific side effects:

    1. Primitive fields in oneOf/anyOf: When switching between schema variants with mergeDefaultsIntoFormData: "useDefaultIfFormDataUndefined", undefined primitive fields (boolean, string, number) will now remain undefined or receive their proper default value, instead of being incorrectly set to an empty object {}.
    2. Optional arrays: Optional arrays are no longer automatically initialized to [] during the formData defaulting phase. They will remain uninitialized if not present in the data.