Skip to content

ui-schema/ui-schema

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

UI Schema Logo

UI Schema

Generate forms and UIs from JSON Schema. Use the headless React components to create powerful schema-driven apps in any design. Get started quickly with Material UI React or Bootstrap and easily create your own widgets and plugins.

Create smart apps faster, with less code — thanks to auto-generated UIs, built-in validation, and easy customization.

Caution

You're exploring the next version! If you spot odd behaviour or have feedback, please open an issue.

The published documentation is still for version 0.4.x. The new documentation for 0.5.x is not yet complete. See this basic migration guide from 0.4.x to 0.5.x. You can preview the new documentation locally by setting up the repository.

Note that the example code below and demo apps are already updated for 0.5.x.

To use the next version you must specify the exact version or use @ui-schema/ui-schema@next during installation.

Github actions Build react compatibility MIT license PRs Welcome Coverage Status Typed

  • @ui-schema/ui-schema npm (scoped)

  • @ui-schema/react npm (scoped)

  • @ui-schema/json-schema npm (scoped)

  • @ui-schema/json-pointer npm (scoped)

  • @ui-schema/ds-material npm (scoped)

  • @ui-schema/ds-bootstrap npm (scoped)

  • @ui-schema/pro npm (scoped)

  • @ui-schema/dictionary npm (scoped)

  • Additional Material-UI Widgets:

  • Additional Packages, not only for UI Schema:

Get Started

Schema

Use JSON Schema to validate data and automatically create UIs with it - UI-Schema makes it easy to write widgets based on schema structures, use custom UI keywords to make it look great!

Schema Documentation

Features

  • add any design-system or custom widget
    • easily create isolated and atomic widgets, with autowired data and validations
    • customize design system behaviour with e.g. widget compositions
    • easy binding of own design systems and custom widgets
    • easily add advanced features like read-or-write mode
  • auto-rendering by data & schema or full-custom forms with autowired widgets
  • flexible translation of widgets
  • modular, extensible and slim core
    • add own plugins
    • add own validators
    • add own base renderers
    • add own widget matchers & render strategies
    • use what you need
  • isomorphic code: for browser, server, and more
  • not just for React, with vanilla-JS core
  • performance optimized, React only updates HTML which must re-render, perfect for big schemas
  • easy nesting for custom object/array widgets with <WidgetEngine/>
  • validate hidden/auto-generated values, virtualize schema levels (hidden keyword)
  • handle store update from anywhere and however you want
  • includes helper functions for store and immutable handling
  • extensive documentations of core, widgets
  • complex conditionals schemas
  • loading / referencing schemas by URL, connect any API or e.g. babel dynamic loading instead
  • definitions and JSON-Pointer references in schemas
  • JSON Schema extension: UI Schema, change design and even behaviour of widgets
  • JSON Schema versions supported: Draft 2020-12, 2019-09 / Draft-08, Draft-07, Draft-06, Draft-04

Design-System and Widgets Overview

Versions

This project adheres to semver, until 1.0.0 and beginning with 0.1.0: all 0.x.0 releases are like MAJOR releases and all 0.0.x like MINOR or PATCH, modules below 0.1.0 should be considered experimental.

Get the latest version - or help build it:

Example UI Schema

First time? Take the quick-start or check out the demo repos!

Example setup of the React engine with @mui as design system, below is a vanilla-HTML text widget.

If you haven’t already set up @mui; then install the @ui-schema dependencies:

npm i --save @ui-schema/ui-schema@next @ui-schema/json-schema@next @ui-schema/json-pointer@next @ui-schema/react@next @ui-schema/ds-material@next

And add the example code:

import React from 'react';
// Import UI providers and render engine, adapters
import { UIStoreProvider, createStore } from '@ui-schema/react/UIStore';
import { storeUpdater } from '@ui-schema/react/storeUpdater';
import { UIMetaProvider, useUIMeta } from '@ui-schema/react/UIMeta';
import { WidgetEngine } from '@ui-schema/react/WidgetEngine';
import { DefaultHandler } from '@ui-schema/react/DefaultHandler';
import { ValidityReporter } from '@ui-schema/react/ValidityReporter';
import { schemaPluginsAdapterBuilder } from '@ui-schema/react/SchemaPluginsAdapter';
import { isInvalid } from '@ui-schema/react/isInvalid';
import { createOrderedMap } from '@ui-schema/ui-schema/createMap';
import { keysToName } from '@ui-schema/ui-schema/Utils/keysToName';
// basic in-schema translator / `t` keyword support
import { translatorRelative } from '@ui-schema/ui-schema/TranslatorRelative';
// Validator engine, validators and adapter plugin to react
import { Validator } from '@ui-schema/json-schema/Validator';
import { standardValidators } from '@ui-schema/json-schema/StandardValidators';
import { requiredValidatorLegacy } from '@ui-schema/json-schema/Validators/RequiredValidatorLegacy';
import { validatorPlugin } from '@ui-schema/json-schema/ValidatorPlugin';
import { requiredPlugin } from '@ui-schema/json-schema/RequiredPlugin';
// Get the widgets binding for your design-system
// import type { MuiBinding } from '@ui-schema/ds-material/BindingType';
import { bindingComponents } from '@ui-schema/ds-material/Binding/Components'
import { widgetsDefault } from '@ui-schema/ds-material/Binding/WidgetsDefault';
import { widgetsExtended } from '@ui-schema/ds-material/Binding/WidgetsExtended';
import { GridContainer } from '@ui-schema/ds-material/GridContainer';
import { SchemaGridHandler } from '@ui-schema/ds-material/Grid'; // MUI v5/v6
// import { GridItemPlugin } from '@ui-schema/ds-material/GridItemPlugin'; // MUI v7
import Button from '@mui/material/Button';

// could be fetched from some API or bundled with the app
const schemaBase = {
    type: 'object',
    properties: {
        country: {
            type: 'string',
            widget: 'Select',
            enum: [
                'mx',
                'my',
                'fj',
            ],
            default: 'fj',
            ttEnum: 'upper',
        },
        name: {
            type: 'string',
            maxLength: 20,
        },
    },
    required: [
        'country',
        'name',
    ],
};

// or fetch from API
const data = {};

export const DemoForm = () => {
    // optional state for display errors/validity
    const [showValidity, setShowValidity] = React.useState(false);

    // needed variables and setters for the render engine, create wherever you like
    const [store, setStore] = React.useState(() => createStore(createOrderedMap(data)));
    const [schema/*, setSchema*/] = React.useState(() => createOrderedMap(schemaBase));

    // `useUIMeta` can be used safely, without performance impact (while `useUIStore` impacts performance)
    const {t} = useUIMeta()

    const onChange = React.useCallback((actions) => {
        setStore(storeUpdater(actions))
    }, [setStore])

    return <>
        <UIStoreProvider
            store={store}
            onChange={onChange}
            showValidity={showValidity}
        >
            <GridContainer>
                <WidgetEngine isRoot schema={schema}/>
            </GridContainer>
        </UIStoreProvider>

        <Button
            /* show the validity only at submit (or pass `true` to `showValidity`) */
            onClick={() => {
                if(isInvalid(store.getValidity())) {
                    setShowValidity(true)
                    return
                }
                console.log('doingSomeAction:', store.valuesToJS())
            }}
        >
            submit
        </Button>
    </>
};

/**
 * @type {MuiBinding}
 */
const customBinding = {
    ...bindingComponents,

    // Widget mapping by schema type or custom ID.
    widgets: {
        ...widgetsDefault,
        ...widgetsExtended,
    },

    // Plugins that wrap each rendered widget.
    widgetPlugins: [
        DefaultHandler, // handles `default` keyword

        // Runs SchemaPlugins, connects to SchemaResource (if enabled)
        schemaPluginsAdapterBuilder([
            // runs `validate` and related schema postprocessing
            validatorPlugin,

            // injects the `required` prop
            requiredPlugin,
        ]),

        SchemaGridHandler, // MUI v5/6 Grid item
        // GridItemPlugin, // MUI v7 Grid item

        ValidityReporter, // keeps `valid`/`errors` in sync with `store`
    ],
};

const validator = Validator([
    ...standardValidators,
    requiredValidatorLegacy, // opinionated validator, HTML-like, empty-string = invalid
])
const validate = validator.validate

export default function App() {
    return <UIMetaProvider
        // the components and widgets bindings for the app
        binding={customBinding}

        // optional, needed for any validation based plugin
        validate={validate}

        // optional, generate labels, error messages,
        // support embedded translations
        t={translatorRelative}

        // optional, enable `name` attribute generation
        keysToName={keysToName}

        // never pass down functions like this - always use e.g. `React.useCallback`
        // or use functions defined outside rendering, check performance docs for more
        //t={(text, context, schema) => {/* add translations */}}
    >
        {/*
          * Use one UIMetaProvider with multiple forms.
          * it's possible to nest `UIMetaProvider` if you need to have different `binding`,
          * e.g. depending on some lazy loaded component tree
          */}
        <DemoForm/>
    </UIMetaProvider>
}

Instead of using a WidgetEngine at root level (automatic rendering of full schema), it's also possible to use full custom rendering.

Example Simple Text Widget

Easily create new widgets, this is all for a simple text (type=string) widget:

import { TranslateTitle } from '@ui-schema/react/TranslateTitle'
import { WidgetProps } from '@ui-schema/react/Widget'

const Widget = (
    {
        value, storeKeys, onChange, keysToName,
        required, schema,
        errors, valid,
    }: WidgetProps,
) => {
    // as any value could come in, from e.g. remote sources,
    // typeguard `value` for the actual input you implement
    const inputValue = typeof value === 'string' || typeof value === 'number' ? value : ''
    return <>
        <label>
            <span><TranslateTitle schema={schema} storeKeys={storeKeys}/></span>

            <input
                type={'text'}
                required={required}
                name={keysToName?.(storeKeys)}
                value={inputValue}
                onChange={(e) => {
                    onChange({
                        storeKeys,
                        scopes: ['value'],
                        // or use another StoreAction like `update`
                        type: 'set',
                        data: {
                            value: e.target.value,
                            //internalValue: undefined
                            //valid: undefined
                        },
                        schema,
                        required,
                    })
                }}
            />
        </label>
    </>
}

Contributing

See CONTRIBUTING.md.

License

Released under the MIT License.

About

Use JSON-Schema with React, generate Forms + UIs with any design system, easy creation of complex custom widgets.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •