/ui-schema

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

Primary LanguageTypeScriptMIT LicenseMIT

UI Schema Logo

UI Schema for React

JSON Schema form + UI generator for any design system, first-class support for Material UI React, easily create own widgets and plugins.

Develop your next React app faster, with less code duplications - and without wasting time to implement validations.

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

  • @ui-schema/ui-schema 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:

    • Date-Time Picker: @ui-schema/material-pickers npm (scoped) Component Documentation
    • Codemirror as Material Input: @ui-schema/material-code npm (scoped) Component Documentation repo
    • react-color picker: @ui-schema/material-color npm (scoped) Component Documentation repo
    • react-colorful picker: @ui-schema/material-colorful npm (scoped) Component Documentation repo
    • 🚧 Drag 'n Drop with react-dnd: @ui-schema/material-dnd npm (scoped) Component Documentation
    • 🚧 EditorJS as Material TextField: @ui-schema/material-editorjs npm (scoped) Component Documentation
    • 🚧 SlateJS as Material TextField: @ui-schema/material-slate npm (scoped) Component Documentation
  • Additional Packages, not only for UI Schema:

    • 🚧 CodeMirror v6 kit: @ui-schema/kit-codemirror npm (scoped) Component Documentation repo
    • 🚧 Drag 'n Drop kit: @ui-schema/kit-dnd npm (scoped) Component Documentation

Documentation

Quick-Start

Schema Examples + Live Editor

Get Help on Slack

🚀 Demo: UI Schema + Material Design + Create React App: Demo Source

Run on CodeSandbox

Run on StackBlitz

Fullscreen Demo


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
  • performance optimized, only updates HTML which must re-render, perfect for big schemas
  • code-splitting, with custom widget mappings / lazy-loading widgets
  • includes helper functions for store and immutable handling
  • easy nesting for custom object/array widgets with PluginStack
  • validate hidden/auto-generated values, virtualize schema levels (hidden keyword)
  • handle store update from anywhere and however you want
  • extensive documentations of core, widgets
  • typed components and definitions for JSON Schema and UI Schema
  • 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 2019-09 / Draft-08, Draft-07, Draft-06, Draft-04

🔥 Professional service & support available, reach out now!

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 take a look into the MUI demo repos: create-react-app & JS (simple) or create-react-app & Typescript (advanced).

Example setup of a renderer, followed by a simple text widget.

Instead of using UIRootRenderer it's also possible to use full custom rendering with e.g. ObjectGroup.

import React from 'react';

// Import Schema UI Provider and Render engine
import {isInvalid} from '@ui-schema/ui-schema/ValidityReporter';
import {createOrderedMap} from '@ui-schema/ui-schema/Utils/createMap';
import {UIStoreProvider, createStore} from '@ui-schema/ui-schema/UIStore';
import {storeUpdater} from '@ui-schema/ui-schema/storeUpdater';
import {UIMetaProvider, useUIMeta} from '@ui-schema/ui-schema/UIMeta';
// new in `0.4.0-alpha.1`:
// import {injectPluginStack} from '@ui-schema/ui-schema/applyPluginStack';
// deprecated since `0.4.0-alpha.1`:
import {UIRootRenderer} from '@ui-schema/ui-schema/UIRootRenderer';
// basic in-schema translator / `t` keyword support
import {relTranslator} from '@ui-schema/ui-schema/Translate/relT';
// Get the widgets binding for your design-system
import {widgets} from '@ui-schema/ds-material/widgetsBinding';
// new in `0.4.0-alpha.1`:
// import {GridContainer} from '@ui-schema/ds-material/GridContainer';

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

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

// for `>=0.4.0-alpha.1`:
// const GridStack = injectPluginStack(GridContainer)

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 (`useUI` has a performance impact)
    const {widgets, t} = useUIMeta()

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

    return <>
        <UIStoreProvider
            store={store}
            onChange={onChange}
            showValidity={showValidity}
        >
            {/*
              * for `>=0.4.0-alpha.1`:
              */}
            {/*<GridStack isRoot schema={schema}/>*}

            {/*
              * deprecated since `0.4.0-alpha.1`:
              */}
            <UIRootRenderer schema={schema}/>
        </UIStoreProvider>

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

export default function App() {
    return <UIMetaProvider
        widgets={widgets}
        t={relTranslator}
        // never pass down functions like this - always use e.g. `React.useCallback`, check performance docs for more
        //t={(text, context, schema) => {/* add translations */}}
    >
        {/*
          * somewhere in `YourRouterAndStuff` are your custom forms,
          * it's possible to nest `UIMetaProvider` if you need to have different widgets,
          * e.g. depending on some lazy loaded component tree
          */}
        <YourRouterAndStuff/>
    </UIMetaProvider>
}

Example Simple Text Widget

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

import React from 'react';
import { TransTitle, WidgetProps, WithScalarValue } from '@ui-schema/ui-schema';

const Widget = (
    {
        value, storeKeys, onChange,
        required, schema,
        errors, valid,
        ...props
    }: WidgetProps & WithScalarValue,
) => {
    return <>
        <label><TransTitle schema={schema} storeKeys={storeKeys}/></label>

        <input
            type={'text'}
            required={required}
            value={value || ''}
            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,
                })
            }}
        />
    </>
}

Contributing

See CONTRIBUTING.md.

License

This project is free software distributed under the MIT License.

See: LICENSE.

© 2022 bemit UG (haftungsbeschränkt)

License Icons

The icons in the badges of the readme's are either from simpleicons or are licensed otherwise:


Created by Michael Becker