This project provides light integration over established React components, trying to keep configurations compatible with original project.
All configurations you can specify in original projects, can be reused here.
- Composite array field (
ui:field>compositeArray) - Collapsible fields (
ui:field>collapsible) - Alternative input fields (
ui:field>altInput) - Typeahead, based on react-bootstrap-typeahead (
ui:field>typeahead) - Async Typeahead based on react-bootstrap-typeahead (
ui:field>asyncTypeahead) - RTE, based on react-rte (
ui:field>rte) - Tables, based on react-bootstrap-table (
ui:field>table) - Multi-typeahead field (
ui:field>multiTypeahead)
- Use
- Composite array field (compositeArray)
- Collapsible fields (collapsible)
- Typeahead, based on react-bootstrap-typeahead (typeahead)
- Async Typeahead based on react-bootstrap-typeahead (asyncTypeahead)
- RTE, based on react-rte (rte)
- Tables, based on react-bootstrap-table (table)
- Multi-typeahead field (multiTypeahead)
- React Day Picker, based on react-day-picker (rdp)
- Contribute
- Support
- License
This project uses internal react-jsonschema-form extension mechanism, through ui:field option in uiSchema. The simplest example of using it out of the box, is like this:
import Form from "react-jsonschema-form";
import fields from "react-jsonschema-form-extras";
ReactDOM.render(<Form fields={fields} />, document.getElementById("app"));If you have additional extensions, that are not part of this project, you can enable them, like this
import Form from "react-jsonschema-form";
import otherFields from "other-fields";
import fields from "react-jsonschema-form-extras";
let allFields = Object.assign({}, fields, otherFields);
ReactDOM.render(<Form fields={allFields} />, document.getElementById("app"));You can load only one field you need if want to keep the bundle small.
import Form from "react-jsonschema-form";
import { TypeaheadField } from "react-jsonschema-form-extras/lib/TypeaheadField";
ReactDOM.render(
<Form fields={{ typeahead: TypeaheadField }} />,
document.getElementById("app")
);If you want multiple fields:
import Form from "react-jsonschema-form";
import { TypeaheadField } from "react-jsonschema-form-extras/lib/TypeaheadField";
import ReactDatePicker from "react-jsonschema-form-extras/lib/ReactDatePicker";
ReactDOM.render(
<Form fields={{ typeahead: TypeaheadField, rdp: ReactDatePicker }} />,
document.getElementById("app")
);This is a simple UI pattern, where you want to separate entering a new value to the array and working with existing values.
The simplest uiSchema configuration would be:
{
"ui:field": "compositeArray",
"inputField": "typeahead",
"arrayField": "table",
"typeahead": {},
"table": {}
}This means the final field will be presented in 2 parts
- initial input with
typeaheadfield - array field in
tableform
You can specify configurations for each field representation independently.
There are only 2 properties needed for it to work
inputFieldfield from form registry to use as a new field input presentationarrayFieldfield from form registry to use to present existing array values
Collapsible helps you to hide content, that might take up too much space on the screen an expand it if user wishes.
The simplest uiSchema configuration would be:
{
"ui:field": "collapsible",
"collapse": {
"field": "table"
}
}This is a hidden table field configuration, which will be presented as collapsed schema title name.
You can customize presentation of collapsible field, with "collapse" object in uiSchema
fieldstringan actual hidden field to usecollapsedboolean- indicating initial state (defaulttrue)iconobjecticons configuration inenabledanddisabledstateenabledstringicon, when the field is shown (defaultglyphicon glyphicon-chevron-down)disabledstringicon, when field is hidden (defaultglyphicon glyphicon-chevron-right)addstringicon, to use in place of an add sign (defaultglyphicon glyphicon-plus-sign)
separatebooleanenable
after collapse menu (defaulttrue)wrapClassNamestringclass name to use on a parent collapse menu div (defaultlead)addTostringarray field name, to which icon will be added enables an add icon, that will be shown besides collapsible iconaddElement(experimental) representation element for add function (for example if you want to show modal on add icon press, here where this would be)function(schema, uiSchema, onChange)that returns React Component to render for add functionstringfielddefinition fromreact-jsonschema-formcatalogue
actions(experimental) allows to add additional actions to collapsible menuarrayofobjectsthat allows to render any kind ofactionyou need, which will be sourced fromformContextallActionsconfigurationcomponentstringname of the component, that will be sourced fromformContext.allActionsobjectpropsobjectadditional properties for rendered component
legend(experimental) allows to add additional information under collapsed fieldstringtext to be rendered under collapsible fieldobjectthat allows to render any kind oflegendyou need, which will be sourced fromformContextlegendsconfigurationcomponentstringname of the component, that will be sourced fromformContext.legendsobjectpropsobjectadditional properties for rendered component
Additional feature of the Collapsible field is to allow adding empty value to hidden array, it's enabled with addTo feature, which can
be either self which assumes that Collapsible field is the target array, or it can be a property field.
Field schema title used as a header of the collapsible action.
Task:
We have a firstName field, which is collapsible and we need to display a LanguageLegend, which would notify user of the language to use.
Solution:
The simplest configuration with schema, uiSchema and formContext would look something like this
import React from "react";
import fields from "react-jsonschema-form-extras"
let schema = {
type: "object",
properties: {
firstName: { type: "string" }
}
}
let uiSchema = {
firstName: {
"ui:field": "collapsible",
collapse: {
field: "StringField",
legend: {
component: "LanguageLegend",
props: {
language: "EN"
}
}
}
}
}
let formContext = {
legends: {
LanguageLegend: (props) => (<h1>Expected {props.language} characters</h1>)
}
}
<Form formContext={formContext} schema={schema} uiSchema={uiSchema} fields={fields}>You want to enter the same field in 2 different ways. For example if a field might be a string and number
The simplest configuration would look something like this
{
"ui:field": "altInput",
"defInput": "typeahead",
"altInput": "asyncTypeahead",
"typeahead": {},
"asyncTypeahead": {}
}In this case user would be able to enter the same field, either by using async typeahead or regular one.
In order to configure presentation there are few options
defInputstringregistry field to use as primary input methodaltInputstringregistry field to use as an alternative input methodaltInputSeparatorstringstring to use in between those 2 presentations
Typeahead, based on react-bootstrap-typeahead (typeahead)
This is a wrap of react-bootstrap-typeahead, which allows you to use this project in your jsonschema-form
The simplest configuration would be
{
"ui:field": "typeahead",
"typeahead": {
"options": [{ "state": "New York" }, { "code": "Washington" }],
"labelKey": "state"
}
}In this case the typeahead would only have 2 options - New York and Washigton
All properties that you specify under typeahead will be used in the original project.
focusOnMountfocusOn typeahead, after it was mounted to pagetypeaheadall properties that you specify undertypeaheadwill be used in the original project. Additionally, there are few project specific propertieslabelKeyhave more flexibility in configurationlabelKeystringused a labelKey in typeahead projectlabelKeyarrayin this case array is a list of fields in original object, which are combined in a single string with a space separatorlabelKeyobjectwithfieldsarrayof fields to use,separatorstring separator to use between fieldscleanAfterSelectionbooleanclean selection after component was selected (default false)mappingobjectthat maps selected object to schema object
For complete list of configurations refer to react-bootstrap-typeahead
Here are some use case examples
With following options
[
{
"name": "Adventures of Huckleberry Finn",
"author": "Mark Twain"
},
{
"name": "The Adventures of Tom Sawyer",
"author": "Mark Twain"
}
]With labelKey name there will be 2 options
Adventures of Huckleberry FinnThe Adventures of Tom Sawyer
With labelKey [ "author", "name" ], options will be
Mark Twain Adventures of Huckleberry FinnMark Twain The Adventures of Tom Sawyer
With lableKey { fields: [ "author", "name" ], separator: " - " }, options will be
Mark Twain - Adventures of Huckleberry FinnMark Twain - The Adventures of Tom Sawyer
Mapping can be one of
- not specified, in this case selection is sent to the formData as is
stringwhich is field name in typeahead selected objectobjectwith fields corresponding to final schema fields and values, corresponding to fields in typeaheadfunctionwhich will be called with typeahead selected objects, which ever value you specify will be used
Mapping as undefined (we accept values as is)
{
"mapping": undefined
}
would result in
{ "name": "Adventures of Huckleberry Finn", "author": "Mark Twain" }{ "name": "The Adventures of Tom Sawyer", "author": "Mark Twain" }
Mapping as string (we want only name of the book)
{
"mapping": "name"
}would result in
"Adventures of Huckleberry Finn""The Adventures of Tom Sawyer"
Mapping as object (we want to change mapping to creator and book)
{
"mapping": {
"creator": "author",
"book": "name"
}
}would result in
{ book: "Adventures of Huckleberry Finn", creator: "Mark Twain" }{ book: "The Adventures of Tom Sawyer", creator: "Mark Twain" }
Mapping as function (let's say we want to take a first name of the author)
let uiSchema = {
mapping: (event) => event.creator.split(" ")[0]
};would result in
"Mark""Mark"
Async Typeahead based on react-bootstrap-typeahead (asyncTypeahead)
This is a wrap around async functionality of typeahead, supporting some additional defaults.
The simplest configuration would be
{
"ui:field": "asyncTypeahead",
"asyncTypeahead": {
"url": "https://example.com/state"
}
}This will result in typeahead search with https://example.com/state?query=${query}
Async typeahead extends default configuration list for typeahead, by adding few properties under asyncTypeahead
focusOnMountfocusOn typeahead, after it was mounted to pageasyncTypeaheadall properties that you specify undertypeaheadwill be used in the original project.urlsearch url, that will be used during autocompletesearchfunction that will be querying server for data, which takes 2 parameters, and must return a Promise with a json resulturlconfigured URLquerytyped query string
optionsPathpath to options array in responselabelKeyhave more flexibility in configurationlabelKeystringused a labelKey in typeahead projectlabelKeyarrayin this case array is a list of fields in original object, which are combined in a single string with a space separatorlabelKeyobjectwithfieldsarrayof fields to use,separatorstring separator to use between fields
cleanAfterSelectionbooleanclean selection after component was selected (default false)overrideOptionsif true, the user can type any text in the input field (or select an option, then modify it), and it will be saved in the RJSF model (default false)mappingobjectthat maps selected object to schema object
For example, let's consider query with Was on url https://example.com/state.
By default field will query results with - https://example.com/state?query=Was.
Let's say we want to override it and query - https://example.com/state?name=Was&maxSize=1.
Here is how we can do that:
let uiSchema = {
"ui:field": "asyncTypeahead",
asyncTypeahead: {
url: "https://example.com/state",
search: (url, query) => fetch(`${url}?name=${query}&maxSize=1`)
}
};That is it.
For complete list of async typeahead configurations refer to react-bootstrap-typeahead
RTE, based on react-rte (rte)
This is a simple field, that allows you to enter RTE text inside your string field.
The simplest configuration would be
{
"ui:field": "rte",
"rte": {
"format": "html"
}
}The only property this field requires is format
formatstringanrteoutput format (defaulthtml)updateOnBlurbooleanallows for RTE update parent form only after edit finished, to minimize calculations and redraw (defaultfalse)
As with other projects, all configurations, that you'll configure under uiSchema rte field will be transferred to the actual component.
Tables, based on react-bootstrap-table (table)
This component wraps react-bootstrap-table for array components, with smart default configurations.
The simplest configuration would be
{
"ui:field": "table"
}You can use table field without any predefined configurations, it will generate default table schema with columns.
tableColsan array of react-bootstrap-table configurations, that override default generated configurations for the field.focusOnAddcolumn number, when set, adding new row to the table, it will focus on a specified column.
By default table component will generate table columns, based on an array schema, with editables, based on field types.
You can reuse react-jsonschema-form Components, in table column editing, to do that, you need to define
fieldproperty in tableCols override section, withuiSchemato use for the field.
For example let's say we have allergy array, with allergyName coming from server source,
we can enable asyncTypeahead on allergyName field in tableCols override like this:
let uiSchema = {
allergies: {
classNames: "col-md-12",
"ui:field": "table",
table: {
tableCols: [
{
dataField: "allergyName",
field: "asyncTypeahead",
uiSchema: {
"ui:field": "asyncTypeahead",
asyncTypeahead: {
bodyContainer: true,
url: "/allergies/typeahead"
}
}
}
]
}
}
};By default order of columns is defined by schema properties field order.
It might be not always reliable, so there is a way to override it.
By default the order will follow order of columns in tableCols configuration.
let schema = {
type: "object",
properties: {
medications: {
type: "array",
items: {
type: "object",
properties: {
dosage: { type: "number" },
name: { type: "string" }
}
}
}
}
};
let uiSchema = {
medications: {
"ui:field": "table",
table: {
tableCols: [
{
dataField: "name"
},
{
dataField: "dosage"
}
]
}
}
};Here although in medications property schema dosage goes before name, it will be shown first due to tableCols order of columns.
react-bootstrap-table provides custom dataFormat for rendering data in columns. We needed to support serialized configuration, so we extended native functionality, with string configuration,
objectdataFormat can be astringwhich translates into field name in the object.date-time&datestringdataFormat is a format of string presentation, that is generated with moment.js
For example, let's say we have an allergies table, with identifier, which consists of some numeric id and string name. When showing to the user, we want to show only name. Here is how we can do this:
let schema = {
type: "object",
properties: {
allergies: {
type: "array",
items: {
type: "object",
properties: {
identified: {
type: "object",
properties: {
id: { type: "string" },
name: { type: "string" }
}
},
added: { type: "date-time" }
}
}
}
}
};
let uiSchema = {
medications: {
"ui:field": "table",
table: {
tableCols: [
{
dataField: "identifier",
dataFormat: "name"
},
{
dataField: "dosage",
dataFormat: "YYYY-MM-DD"
}
]
}
}
};In this case dataFormat on identifier field, will translate into selecting name field in identifier object.
If you need to define additional column actions to the left or to the right of column content you can do that in
a standard way with uiSchema with leftActions or rightActions defined
let uiSchema = {
table: {
leftActions: [
{
action: "delete",
className: "col-md-1",
columnClassName: "col-md-1",
editColumnClassName: "col-md-1",
icon: "glyphicon glyphicon-minus"
}
],
rightActions: [
{
action: "delete",
icon: "glyphicon glyphicon-minus",
text: "Remove"
}
]
}
};Both left and right actions can accept full column configuration, that will be appended to the rendered column,
For example, to define delete on each column, you can:
let uiSchema = {
table: {
leftActions: [
{
dataField: "delete-button",
dataFormat: (cell, row, enumObject, rowIndex, formData, onChange) => (
<span
onClick={() => onChange(formData.filter((el, i) => rowIndex !== i))}
>
"Remove All"
</span>
),
editable: false,
displayName: "Left Panel"
}
],
rightActions: [
{
action: "delete",
icon: "glyphicon glyphicon-minus",
text: "Delete",
displayName: "Right Panel"
}
]
}
};In left panel, we have defined delete with a standard react-bootstrap-table format, the only difference is dataFormat signature changed,
appending original formData and onChange callback to relay changes to the listening component.
Right panel is defined with small syntactic sugar to simpify action defintion
actioncan be eitherdeletestring or function, that is equivalent on onClick function withcell,row,enumObject,rowIndex,formData,onChangeparametersiconicon to use for the columntexttext to use for the columndisplayNamecolumn name
This component provides a multi-typeahead dropdown using Material-UI components with multiple selection enabled. It allows users to search and select multiple options, displaying them as chips/tags, and integrates with react-jsonschema-form. Compatible with react-jsonschema-form.
- BREAKING CHANGE: Renamed from
MultiSelectFieldtoMultiTypeaheadField - Field Name: Changed from
ui:field: "multiSelect"toui:field: "multiTypeahead" - Configuration: Changed from
multiSelect: {}tomultiTypeahead: {}in uiSchema - Uses Material-UI components for multi-typeahead functionality.
- All styling (border, label, placeholder, chip, dropdown options, icons) is handled via JSS (
withStyles). - Consistent color for label, placeholder, dropdown options, chips, and icons.
- Placeholder and label are always centered and use color
#003B5CBF. - Chips use background
#00629Band white text; delete icon is white. - Dropdown and clear icons use dark blue (
#00629B). - Input and chip layout is compact, with no extra lines or spacing.
- Fixed chip grey appearance on click/hover with proper state overrides.
- Fixed React key prop issues to prevent visual glitches when deleting chips.
The simplest uiSchema configuration would be:
{
"ui:field": "multiTypeahead",
"multiTypeahead": {
"options": [
{ "label": "Option 1", "value": "opt1" },
{ "label": "Option 2", "value": "opt2" }
],
"label": "Choose options",
"placeholder": "Select..."
}
}All properties are configured under the multiTypeahead object in uiSchema:
options(array): Static list of options to display. Can be an array of objects or strings.url(string): URL for API-based options. When provided, options will be fetched dynamically based on user input with 300ms debounce.search(function): Custom search function that overrides the default fetch behavior. Takes(url, query, queryKey)parameters and must return a Promise.queryKey(string): Query parameter key used in API requests (default: "query"). For example, withqueryKey: "search", the URL becomes${url}?search=${query}.optionsPath(string): Path to extract options array from API response. Use dot notation for nested properties (e.g.,"data.results","response.items").label(string): Optional label for the field.placeholder(string): Optional placeholder text (default: "Select...").labelTemplate(string): Template for displaying both option labels in the dropdown and chip labels. Use{fieldName}syntax to reference object properties (e.g.,"{name} - {category}"). Required for object values - if not provided, both options and chips will display as empty when dealing with object values. For string values, the string itself will be displayed regardless of template. This template uses the flattened data structure created byvalueKeys.valueKeys(array): Array of keys to extract from selected options for the form value (default:["value"]). Note: When using nested property paths (e.g.,"user.profile.name"), only the last part of the key chain ("name") will be used as the property name in the resulting value object. The selected data is flattened based on these keys before being stored in formData. Both dropdown options and chips use this flattened structure.
- Dual Mode Support: Works with both static options and dynamic API-based options
- Unified Templates: Use
labelTemplateto customize how both dropdown options and chips are displayed. Both use the same flattened data structure fromvalueKeys. - Flexible Values:
valueKeysallows you to control which properties are saved in the form data and flattens the data structure for consistent display - Nested API Responses: Use
optionsPathto extract options from complex API response structures - Custom Query Parameters: Use
queryKeyto customize the API query parameter name - Search & Filter: For static options, filters locally; for URL-based options, searches via API
- Debounced API Calls: 300ms debounce prevents excessive API requests during typing
- Persistent Selections: Selected values remain visible even when not in current search results
- Clear Functionality: Clear individual chips or all selections at once
- Loading States: Shows loading indicator during API calls
- Keyboard Accessible: Full keyboard navigation support
- Click-away Handling: Dropdown closes when clicking outside the component
import Form from "react-jsonschema-form";
import { MultiTypeaheadField } from "react-jsonschema-form-extras/lib/MultiTypeaheadField";
const fields = { multiTypeahead: MultiTypeaheadField };
const schema = {
type: "object",
properties: {
fruits: {
type: "array",
items: { type: "object" }
}
}
};
const uiSchema = {
fruits: {
"ui:field": "multiTypeahead",
multiTypeahead: {
options: [
{ name: "Apple", category: "Tree Fruit", value: "apple", id: 1 },
{ name: "Banana", category: "Tropical", value: "banana", id: 2 },
{ name: "Cherry", category: "Stone Fruit", value: "cherry", id: 3 }
],
label: "Select Fruits",
placeholder: "Choose fruits...",
labelTemplate: "{name} - {category}",
valueKeys: ["id", "value", "name", "category"]
}
}
};
<Form schema={schema} uiSchema={uiSchema} fields={fields} />;const uiSchema = {
medications: {
"ui:field": "multiTypeahead",
multiTypeahead: {
url: "/api/medications/search",
queryKey: "search", // Use "search" instead of default "query"
optionsPath: "data.medications", // Extract from nested response
label: "Select Medications",
placeholder: "Type to search medications...",
labelTemplate: "{name} ({strength})",
valueKeys: ["id", "name", "strength"],
// Optional custom search function
search: (url, query, queryKey) => {
return fetch(
`${url}?${queryKey}=${encodeURIComponent(query)}&limit=20`
).then((res) => res.json());
// No need to extract data.results here, optionsPath will handle it
}
}
}
};
// Without queryKey specified (defaults to "query") but with optionsPath
const uiSchemaWithOptionsPath = {
users: {
"ui:field": "multiTypeahead",
multiTypeahead: {
url: "/api/users/search", // Will call: /api/users/search?query=userInput
optionsPath: "response.users", // Extract from { response: { users: [...] } }
labelTemplate: "{firstName} {lastName}",
valueKeys: ["id", "username", "firstName", "lastName"]
}
}
};
// Simple case - API returns array directly (no optionsPath needed)
const uiSchemaDefault = {
tags: {
"ui:field": "multiTypeahead",
multiTypeahead: {
url: "/api/tags/search", // API returns: [{ name: "tag1" }, { name: "tag2" }]
labelTemplate: "{name}",
valueKeys: ["id", "name"]
}
}
};const schema = {
type: "object",
properties: {
tags: {
type: "array",
items: { type: "string" }
}
}
};
const uiSchema = {
tags: {
"ui:field": "multiTypeahead",
multiTypeahead: {
options: ["JavaScript", "React", "Node.js", "Python", "Java"],
label: "Programming Languages",
placeholder: "Select languages..."
}
}
};// Options with nested properties (original structure)
const options = [
{
user: { profile: { name: "John Doe" }, id: 123 },
department: { info: { title: "Engineering" } },
role: "Developer"
}
];
const uiSchema = {
employees: {
"ui:field": "multiTypeahead",
multiTypeahead: {
options: options,
// labelTemplate uses FLATTENED data structure (after valueKeys processing)
// Both dropdown options and chips use the same flattened structure
labelTemplate: "{name} - {role}",
valueKeys: [
"user.profile.name",
"user.id",
"department.info.title",
"role"
]
}
}
};
// How it works:
// 1. Options are transformed once using valueKeys to create flattened structure:
// {
// name: "John Doe", // from user.profile.name (uses last part: "name")
// id: 123, // from user.id (uses last part: "id")
// title: "Engineering", // from department.info.title (uses last part: "title")
// role: "Developer" // from role (uses last part: "role")
// }
// 2. Both dropdown and chips show: "John Doe - Developer" (using labelTemplate with flattened structure)
// 3. FormData contains the same flattened structure as displayedAll styles are handled via JSS (withStyles) in the component. The styling is designed to be consistent and follows Material-UI design patterns with custom color overrides:
Input Field:
- Border color:
#DFE6EB - Font family: Mulish
- Input text color:
#003B5C - Label/placeholder color:
#003B5CBF - Minimum height: 56px with centered alignment
Chips (Selected Items):
- Background:
#00629B(blue) - Text color: White
- Delete icon: White
- Font: Mulish, 12px
- Proper state handling for hover/focus/active states
Dropdown:
- Background: White
- Border:
#DFE6EB - Border radius: 4px (bottom only)
- Box shadow:
0 2px 8px rgba(0,0,0,0.1) - Max height: 200px with scroll
- Option hover:
#f5f5f5
Icons:
- Dropdown indicator:
#00629B - Clear button:
#00629B - Loading indicator: Displays during API calls
Layout:
- Chips and input field are properly aligned in a flex container
- Input field takes remaining space with minimum 120px width
- Clear button appears when selections exist
- Compact layout with optimized spacing
To customize styling, edit the styles object in MultiTypeaheadField.js using JSS syntax.
React Day Picker, based on react-day-picker (rdp)
Allows you to use react-day-picker as input ui:field. This component works only with string formatted as date and date-time.
The simplest configuration would be
{
"ui:field": "rdp"
}All configurations, that you'll configure under uiSchema rdp field will be transferred to the actual component.
For example to enable Today button, you would need to specify following uiSchema
{
"ui:field": "rdp",
"rdp": {
"dayPickerProps": {
"todayButton": "Today"
}
}
}For the full list of properties refer to React Day Picker.
to enable expandable row in table component
to expand table rows
{
"table": {
"isTableExpandable": false,
"allowOneRowExpanding": true
}
}All configurations, that you'll configure under uiSchema table field will be transferred to the actual component.
For example to enable ExpandRow button, you would need to specify following uiSchema
{
"table": {
"isTableExpandable": false,
"allowOneRowExpanding": true
}
}- Issue Tracker: github.com/RxNT/react-jsonschema-extras/issues
- Source Code: github.com/RxNT/react-jsonschema-extras
If you are having issues, please let us know here or on StackOverflow.
The project is licensed under the Apache Licence 2.0.