Generate forms and UIs from JSON Schema. Use the headless React components to create powerful schema-driven apps, compatible with any design system. 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. However, the repository includes a basic migration guide from 0.4.x to 0.5.x, and the example code below is updated for 0.5.x. You can preview the new documentation locally by setting up the repository.
To use the next
version you must specify the exact version or use @ui-schema/ui-schema@next
during installation.
-
Additional Material-UI Widgets:
-
Additional Packages, not only for UI Schema:
🚀 Demo: UI Schema + Material Design + Create React App: Demo Source
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!
- 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
- with any library (
t
prop (Translator),Translate
/TranslateTitle
components) - in-schema translations (
t
keyword) - label text transforms (
tt
/ttEnum
keyword) - single or multi-language
- for labels, titles, errors, icons...
- (optional) tiny integrated translation library
- (optional) translation dictionaries
- with any library (
- 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
WidgetEngine
- 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
Design-System and Widgets Overview
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:
- latest releases (GitHub release notes)
- update and migration notes (docs page)
- current roadmap (GitHub discussion)
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 the React engine, followed by a simple text widget - with @mui as design system.
If you haven’t already set up @mui, install it first; 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/ds-material@next
Then 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/Binding';
import { baseComponents, typeWidgets } from '@ui-schema/ds-material/BindingDefault';
import { bindingExtended } from '@ui-schema/ds-material/BindingExtended';
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 = {
...baseComponents,
// Widget mapping by schema type or custom ID.
widgets: {
...typeWidgets,
...bindingExtended,
},
// 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.
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>
</>
}
See CONTRIBUTING.md.
Released under the MIT License.
The icons in the badges of the readme's are either from simpleicons or are licensed otherwise: