React JSON Schema Form Builder

repository·main·Indexed 18 days ago

https://github.com/ginkgobioworks/react-json-schema-form-builder

A React component for visually configuring JSON Schema-based forms through a drag-and-drop interface. It generates standard JSON Schema and UI Schema objects compatible with react-jsonschema-form, supporting features like conditional logic (dependencies), reusable definitions via $ref, and custom input types. Version 4.1.0 requires React 19+, Node.js 20+, and Material-UI 7+.

Tokens
9.5K
Snippets
25
Records
37
Agent score
63%

What's inside @ginkgo-bioworks/react-json-schema-form-builder

  1. Overview of React JSON Schema Form Builder

    main

    The FormBuilder is a React component designed for visual form configuration. It enables users to build complex forms by dragging, dropping, and editing card elements that represent JSON Schema structures.

    Key features include:

    • Visual Configuration: Drag-and-drop interface for building forms.
    • JSON Schema Support: Generates standard JSON Schema and UI Schema objects.
    • Customizability: Developers can incorporate novel form elements (e.g., specialized email inputs or file uploads) into the builder.
    • Real-time Sync: Designed to work alongside code editors and viewers (like Mozilla's React JSON Schema Form) to maintain a live code representation of the visual form.
  2. Configure Form Element properties

    main

    Every form element in the builder is defined by several key properties:

    • Object Name: The key under which the data entered into this element will be stored in the final output JSON.
    • Display Name: The label shown to the end-user filling out the form. If left empty, the Object Name is used as the default.
    • Description: A smaller body of text displayed below the name to provide context to the user.
    • Input Type: Determines the type of UI component used to collect data (e.g., Short Answer, Number, etc.).
    • Required: A checkbox in the element footer that, when checked, mandates the user to provide a value before successful submission.
  3. Implement conditional logic with Dependencies

    main

    The Form Builder allows you to create conditional visibility using a Parent/Dependent model, which abstracts the React JSON Schema Form dependencies feature.

    How it works

    • Parent: The element that controls the visibility of others.
    • Dependent: The element that is hidden or shown based on the parent's state.
    • Possibilities: Within the element's modal, you can create "possibilities" to define the logic.

    Configuration Options

    When setting up a parent, you can choose how the dependency is triggered:

    1. Any value: The dependent elements appear if the parent has any value entered.
    2. Specific value: The dependent elements appear only when the parent matches a specific value (allowing for multiple different scenarios/possibilities).

    Visual Indicators

    Dependent elements are rendered with dashed borders and an asterisk to indicate they are currently hidden and waiting for the parent's condition to be met.

  4. How the Input Type Matching Algorithm works

    main

    The Form Builder uses the matchIf array within a FormInput to determine which input type to assign to a field when loading a schema. Each object in the matchIf array is a Match object representing a specific scenario.

    A match is successful if the field's schema meets the following criteria:

    • types: The field's JSON Schema DataType is in this array.
    • widget: The ui:widget value in the UI schema matches.
    • field: The ui:field value in the UI schema matches.
    • format: The format property in the Data schema matches.
    • $ref: The data schema contains a $ref property.
    • enum: The data schema contains an enum property.
  5. Customize Form Builder with Mods

    main

    The Mods object is passed to the FormBuilder component via the mods prop to customize its behavior, appearance, and available input types.

    Key customization capabilities include:

    • Custom Inputs: Define new input types via customFormInputs.
    • UI Customization: Hide default inputs with deactivatedFormInputs, customize labels with labels, or change tooltip text with tooltipDescriptions.
    • Component Overrides: Replace the "Add" button component using components.add.
    • Defaults: Set default data or UI schema for new elements using newElementDefaultDataOptions or newElementDefaultUiSchema.
    • Visibility: Toggle the form header using showFormHead (defaults to true).
    import { FormBuilder, type Mods } from '@ginkgo-bioworks/react-json-schema-form-builder';
    
    const myMods: Mods = {
      showFormHead: false,
      deactivatedFormInputs: ['number'],
      // ... other mod configurations
    };
    
    <FormBuilder mods={myMods} />
  6. Understand the Form Builder core abstraction

    main

    The Form Builder is a visual tool designed to generate JSON schemas compatible with the JSON Schema Form component.

    Instead of manual coding, the builder parses the properties, definitions, and dependencies sections of a standard JSON Schema into discrete, editable visual elements. This allows users to rearrange and edit form structures through a drag-and-drop interface rather than editing raw JSON code.

  7. Create custom form inputs using the `mods` property

    main

    You can extend the FormBuilder with custom input types by defining a FormInput object and passing it into the mods property. A custom input translates abstract input types into specific JSON Schema and UI Schema configurations.

    Key types for implementation:

    • FormInput: The definition of the custom input.
    • Match: Used in FormInput.matchIf to define when this input should be used (e.g., matching a specific type or widget).
    • DataType: Valid JSON Schema types ('string' | 'number' | 'boolean' | 'integer' | 'array' | 'object' | 'null').
    • CardComponent: The component rendered in the cardBody (the builder UI) or modalBody (the configuration UI).
    • CardComponentProps: The props passed to these components, including parameters (the current schema/ui-schema state) and onChange (to update the state).
    import type { FormInput, Match, DataType, CardComponent, CardComponentProps } from '@ginkgo-bioworks/react-json-schema-form-builder';
    
    const myCustomInput: FormInput = {
      displayName: 'My Custom Input',
      matchIf: [{ types: ['string'], widget: 'my-widget' }],
      defaultDataSchema: { type: 'string' },
      defaultUiSchema: { 'ui:widget': 'my-widget' },
      type: 'string',
      cardBody: ({ parameters, onChange }) => (
        // Implementation of the builder UI
        <div />
      ),
      modalBody: ({ parameters, onChange }) => (
        // Implementation of the configuration modal
        <div />
      ),
    };
    
    // Usage in FormBuilder
    <FormBuilder 
      mods={{ 
        customFormInputs: { myInput: myCustomInput } 
      }} 
      {...otherProps} 
    />
  8. Organize forms using Cards and Sections

    main

    The builder uses two primary structural components to organize the form:

    Cards

    Cards represent individual form elements. They are the smallest unit of the form and contain a single input type (unless they are an Array). Cards can be rearranged via drag-and-drop or using the arrow buttons in their header.

    Sections

    Sections are larger containers used to group multiple Cards together.

    • To create a section, select "Form section" from the element creation menu.
    • Sections do not have an input type themselves; they exist solely to hold Cards or other Sections.
    • To add an element inside a section, you must click the "+" button located within that specific section.
    • Moving a Section will move all contained Cards along with it.
  9. Use Definitions and $ref for reusable elements

    main

    The Form Builder supports JSON Schema definitions and the use of $ref tags to promote reusability.

    • Enabling References: The option to use a definition as an input type only appears in the Input Types dropdown if the underlying schema already contains at least one definition.
    • Propagation: When an element is set to reference a definition, any changes made to that central definition will automatically propagate to all elements referencing it.
    • Local Overrides: The builder supports local overrides to $ref titles and descriptions, consistent with modern react-jsonschema-form capabilities.
    • PredefinedGallery: An optional component (available in the Usage.md guide) that allows users to visually build these definitions.
  10. Install @ginkgo-bioworks/react-json-schema-form-builder

    main

    To use the Form Builder in your project, install the core package along with its required peer dependencies: Material-UI (MUI) and Emotion.

    Requirements:

    • React 19+
    • Node.js 20+ (LTS)
    • Material-UI (MUI) 7+
    npm i --save @ginkgo-bioworks/react-json-schema-form-builder @mui/material @emotion/react @emotion/styled
  11. Integrate FormBuilder with React JSON Schema Form (RJSF)

    main

    To provide a real-time preview of the form being built, you can pair FormBuilder with an implementation of the Form component from react-jsonschema-form (RJSF).

    First, install the necessary RJSF dependencies:

    npm i --save @rjsf/core @rjsf/mui @rjsf/validator-ajv8

    Then, use the schema and uischema produced by FormBuilder to drive the RJSF Form component. Note that FormBuilder provides these as strings, so they must be parsed using JSON.parse() before being passed to the RJSF Form.

    import React, { useState } from 'react';
    import { FormBuilder } from '@ginkgo-bioworks/react-json-schema-form-builder';
    import { withTheme } from '@rjsf/core';
    import { Theme as MuiTheme } from '@rjsf/mui';
    import validator from '@rjsf/validator-ajv8';
    
    const Form = withTheme(MuiTheme);
    
    function Example() {
      const [schema, setSchema] = useState('{}');
      const [uischema, setUiSchema] = useState('{}');
      const [formData, setFormData] = useState({});
    
      return (
        <div>
          <FormBuilder
            schema={schema}
            uischema={uischema}
            onChange={(schema, uischema) => {
              setSchema(schema);
              setUiSchema(uischema);
            }}
          />
          <Form
            schema={JSON.parse(schema)}
            uiSchema={JSON.parse(uischema)}
            onChange={(data) => setFormData(data.formData)}
            formData={formData}
            validator={validator}
          />
        </div>
      );
    }
    
    export default Example;
  12. Customize 'Add' buttons and labels

    main

    You can customize the behavior and appearance of the 'Add' buttons (the + buttons) in several ways:

    1. Change Labels

    Update the text used for adding elements or sections via the labels object in mods.

    2. Programmatic Addition

    You can trigger the addition of elements or sections from anywhere in your application using addCardObj or addSectionObj. These functions require an object of type AddFormObjectParameters.

    To get the necessary categoryHash required by these functions, use the onMount callback on the FormBuilder component to capture the InitParameters.

    3. Override the Add Component

    To completely replace the UI of the 'Add' buttons, provide a callback function to mods.components.add. This callback receives AddFormObjectParameters and should return a React component.

    import { 
      FormBuilder, 
      addCardObj, 
      addSectionObj, 
      type AddFormObjectParameters, 
      type InitParameters 
    } from '@ginkgo-bioworks/react-json-schema-form-builder';
    import Button from '@mui/material/Button';
    import Stack from '@mui/material/Stack';
    
    function MyFormBuilder() {
      const [categoryHash, setCategoryHash] = useState('');
    
      const mods = {
        components: {
          add: (addProps: AddFormObjectParameters) => (
            <Stack direction="row" spacing={1}>
              <Button onClick={() => addCardObj(addProps)}>Add Element</Button>
              <Button onClick={() => addSectionObj(addProps)}>Add Section</Button>
            </Stack>
          ),
        },
      };
    
      return (
        <FormBuilder
          onMount={(params: InitParameters) => setCategoryHash(params.categoryHash || '')}
          mods={mods}
          // ... other props
        />
      );
    }