# json-edit-react
A highly-configurable [React](https://github.com/facebook/react) component for editing or viewing JSON/object data
## ๐๏ธ [Explore the Demo](https://carlosnz.github.io/json-edit-react/)



[](https://deepwiki.com/CarlosNZ/json-edit-react)
[](https://github.com/CarlosNZ/json-edit-react/discussions)
[](https://github.com/sponsors/CarlosNZ)
### Features include:
- โ
**Easy inline editing** of individual values or whole blocks of JSON text
- ๐ **Granular control** โ restrict edits, deletions, or additions per element
- ๐ **[JSON Schema](https://json-schema.org/) validation** (using 3rd-party validation library)
- ๐จ **Customisable UI** โ built-in or custom [themes](#appearance--theming), CSS overrides or targeted classes
- ๐ฆ **Self-contained** โ plain HTML/CSS, no external UI library dependencies, and *zero runtime dependencies*
- ๐ **Search & filter** โ find data by key, value or custom function
- ๐ง **[Custom components](#custom-nodes--components)** โ replace keys and/or values with specialised components (e.g. date picker, links, images, `undefined`, `BigInt`, `Symbol`)
- ๐ **[Localisation](#localisation)** โ easily translate UI labels and messages
- ๐ **[Drag-n-drop](#drag-and-drop-reordering)** re-ordering within objects/arrays
- ๐น **[Keyboard customisation](#keyboard-control)** โ define your own key bindings
- ๐ฎ **[External control](#programmatic-control)** via callbacks and imperative methods
๐ก Try the **[Live Demo](https://carlosnz.github.io/json-edit-react/)** to see these features in action!
> [!IMPORTANT]
> This documentation is for V2 of **json-edit-react**. Looking for V1? See the [V1 docs](https://github.com/CarlosNZ/json-edit-react/tree/v1.x#readme) and [V1 demo](https://carlosnz.github.io/json-edit-react-v1/).
>
> If you're upgrading from V1, be sure to read the [migration guide](https://github.com/CarlosNZ/json-edit-react/blob/main/migration-guide.md).
>
> ๐ค๏ธ Got feedback? [Open an issue](https://github.com/CarlosNZ/json-edit-react/issues), or join the conversation in [Discussions](https://github.com/CarlosNZ/json-edit-react/discussions/categories/announcements).
## Optional Companion Packages
- **THEMES**: [@json-edit-react/themes](https://www.npmjs.com/package/@json-edit-react/themes) โ curated, ready-made [themes](#using-a-prebuilt-theme-json-edit-reactthemes)
- **COMPONENTS**: [@json-edit-react/components](https://www.npmjs.com/package/@json-edit-react/components) - custom node components (links, date picker, etc.)
- **UTILITIES**: [@json-edit-react/utils](https://www.npmjs.com/package/@json-edit-react/utils) โย a collection of helpers and tools for extending and streamlining your **json-edit-react** experience
----
## Contents
- [Using the editor (for end users)](#using-the-editor-for-end-users)
- [Installation](#installation)
- [Implementation](#implementation)
- [Props Reference](#props-reference)
- [Managing state](#managing-state)
- [Filter Functions](#filter-functions)
- [Controlling editing](#controlling-editing)
- [Reacting to changes](#reacting-to-changes)
- [Appearance \& theming](#appearance--theming)
- [Initial expansion โ `collapse`](#initial-expansion--collapse)
- [Search \& filtering](#search--filtering)
- [Localisation](#localisation)
- [Keyboard control](#keyboard-control)
- [Custom Nodes \& Components](#custom-nodes--components)
- [Overriding and extending the UI](#overriding-and-extending-the-ui)
- [Programmatic control](#programmatic-control)
- [Performance considerations](#performance-considerations)
- [Undo functionality](#undo-functionality)
- [Exported helpers \& types](#exported-helpers--types)
- [Issues \& support](#issues--support)
- [Inspiration](#inspiration)
- [Changelog](#changelog)
## Using the editor (for end users)
It's pretty self explanatory (click the "edit" icon to edit, etc.), but there are a few not-so-obvious ways of interacting with the editor:
- **Double-click** a value (or a key) to edit it
- When editing a string, use Cmd/Ctrl-Enter or Shift-Enter to add a new line (Enter submits the value)
- It's the opposite when editing a full object/array node (which you do by **clicking "edit"** on an object or array value) โ Enter for new line, and Cmd/Ctrl/Shift-Enter for submit
- Escape to **cancel** editing
- Use Tab/Shift-Tab to quickly move from one value to another when editing
- When clicking the "**clipboard**" icon, holding down Cmd/Ctrl will copy the *path* to the selected node rather than its value
- When opening/closing a node, hold down Alt/Option to open/close *all* child nodes at once
- For Number inputs, the โ / โ arrow keys will increment/decrement the value
- For Boolean inputs, the Space bar will toggle the value
- Easily navigate to the next or previous node for editing using the Tab/Shift-Tab keys.
- **Drag and drop** items to change the structure or modify display order
- When editing is not permitted, double-clicking a string value will expand the text to the full value if it is truncated due to length (there is also a clickable "..." for long strings)
- **JSON text input** can accept "looser" input, if an additional JSON parsing method is provided (e.g. [JSON5](https://json5.org/)). See `jsonParse` prop.
[Have a play with the Demo app](https://carlosnz.github.io/json-edit-react/) to get a feel for it!
## Installation
```sh
# Depending on your package manager:
npm i json-edit-react
# OR
yarn add json-edit-react
# OR
pnpm add json-edit-react
```
## Implementation
```jsx
import { JsonEditor } from 'json-edit-react'
// In your React component:
return (
### Data Management
| Prop | Type | Default | Description |
| --------------------- | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data` | `object\|array` | - | The data to be displayed / edited |
| `setData` | `object\|array => void` | - | Method to update your `data` object. **Required.** See [Managing state](#managing-state) below for additional notes. |
| `onUpdate` | `UpdateFunction` | - | A function to run whenever a value is changed in the editor โ edit, add, delete, rename *or* move. See [Update functions](#onupdate--accept-reject-transform). |
| `onChange` | `OnChangeFunction` | - | A function to modify/constrain user input as they type โ see [OnChange functions](#onchange--validating-each-keystroke). |
| `onError` | `OnErrorFunction` | - | A function to run whenever the component reports an error โ see [OnErrorFunction](#onerror). |
| `showClipboardButton` | `boolean` | `true` | Show or hide the "Copy to clipboard" button in the UI. |
| `onCopy` | `OnCopyFunction` | - | A function to run whenever an item is **copied** to the clipboard โ see [Copy Function](#oncopy). |
### Restricting Editing
| Prop | Type | Default | Description |
| -------------------- | ------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowEdit` | `boolean\|FilterFunction` | `true` | If `false`, no editing at all is permitted. A callback function can be provided (return `true` to permit a given node) โ see [Advanced Editing Control](#filter-functions) |
| `allowDelete` | `boolean\|FilterFunction` | `true` | As with `allowEdit` but for deletion |
| `allowAdd` | `boolean\|FilterFunction` | `true` | As with `allowEdit` but for adding new properties |
| `allowTypeSelection` | `boolean\|TypeOptions\|TypeFilterFunction` | `true` | Controls which data types the user can select, including [Custom Node](#custom-nodes--components) types, and **Enums** โ see [Data Type Restrictions](#restricting-data-types--allowtypeselection) |
| `newKeyOptions` | `string[] \| NewKeyOptionsFunction` | - | New keys can be restricted to certain values โ see [New Key Restrictions & Default Values](#new-key-restrictions--default-values) |
| `defaultValue` | `any\|DefaultValueFunction` | `null` | Value that new properties are initialised with โ see [New Key Restrictions & Default Values](#new-key-restrictions--default-values) |
| `allowDrag` | `boolean\|FilterFunction` | `false` | Set to `true` to enable drag and drop functionality โ see [Drag-n-drop](#drag-and-drop-reordering) |
### Look and Feel / UI
| Prop | Type | Default | Description |
| ----------------------- | --------------------------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `theme` | `ThemeInput` | `defaultTheme` | Either one of the built-in themes (imported separately), or an object specifying some or all theme properties โ see [Themes](#appearance--theming). |
| `showIconTooltips` | `boolean` | false | Display tooltips on the icon controls and the other labelled controls when hovering. |
| `indent` | `number` | `3` | Specify the amount of indentation for each level of nesting in the displayed data. |
| `collapse` | `boolean\|number\|FilterFunction` | `3` | Defines which depth level of the data tree will be displayed "expanded" in the UI on initial load โ see [Collapse](#initial-expansion--collapse). |
| `collapseAnimationTime` | `number` | `300` | Time (in ms) for the transition animation when collapsing collection nodes. |
| `collapseClickZones` | `Array<"left" \| "header" \| "property">` | `["left", "header"]` | Aside from the `โ` icon, you can specify other regions of the UI to be clickable for collapsing/opening a collection. |
| `rootName` | `string` | `"data"` | A name to display in the editor as the root of the data object. |
| `showArrayIndexes` | `boolean` | `true` | Whether or not to display the index (as a property key) for array elements. |
| `arrayIndexStart` | `0 \| 1` | `0` | The number the *first* array element's index label starts from. |
| `showStringQuotes` | `boolean` | `true` | Whether or not to display string values in "quotes". |
| `showCollectionCount` | `boolean\|"when-collapsed"\|"when-collapsed-or-filtered"` | `"when-collapsed-or-filtered"` | Whether or not to display the number of items in each collection (object or array). |
| `stringTruncateLength` | `number` | `250` | String values longer than this many characters will be displayed truncated (with `...`). The full string will always be visible when editing. |
| `sortKeys` | `boolean\|CompareFunction` | `false` | If `true`, object keys will be ordered (using default JS `.sort()`). A [compare function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) can also be provided to define sorting behaviour, except the input type should be a tuple of the key and the value of a node i.e. `(a: [string \| number, ValueData], b: [string \| number, ValueData]) => number` |
| `minWidth` | `number\|string` (CSS value) | `250` | Minimum width for the editor container. |
| `maxWidth` | `number\|string` (CSS value) | `600` | Maximum width for the editor container. |
| `baseFontSize` | `number\|string` (CSS value) | `16px` | The "base" font size from which all other sizings are derived (in `em`s). By changing this you will scale the entire component. |
| `insertAtTop` | `boolean\|"object"\|"array"` | `false` | If `true`, inserts new values at the *top* rather than bottom. Can set the behaviour just for arrays or objects by setting to `"object"` or `"array"` respectively. |
| `errorDisplayTime` | `number` | `2500` | Time (in ms) to display the error message in the UI. | |
| `showErrorMessages` | `boolean ` | `true` | Whether or not the component should display its own error messages (you'd probably only want to disable this if you provided your own [`onError` function](#onerror)) |
### Search and Filtering
| Prop | Type | Default | Description |
| -------------------- | --------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------ |
| `searchText` | `string` | `undefined` | Data visibility will be filtered by matching against value, using the method defined below in `searchFilter` |
| `searchFilter` | `"key"\|"value"\|"all"\|SearchFilterFunction` | `undefined` | Define how `searchText` should be matched to filter the visible items โ see [Search/Filtering](#search--filtering) |
| `searchDebounceTime` | `number` | `350` | Debounce time when `searchText` changes |
### Custom components & overrides (incl. Localisation)
| Prop | Type | Default | Description |
| ----------------------- | --------------------------------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customNodeDefinitions` | `CustomNodeDefinition[]` | | You can provide custom React components to override specific nodes in the data tree, according to a condition function โ see [Custom nodes](#custom-nodes--components) or browse the [@json-edit-react/components](https://www.npmjs.com/package/@json-edit-react/components) |
| `customButtons` | `CustomButtonDefinition[]` | `[]` | You can add your own buttons to the Edit Buttons panel if you'd like to be able to perform a custom operation on the data โ see [Custom Buttons](#custom-buttons) |
| `translations` | `LocalisedStrings` | `{ }` | UI strings (such as error messages) can be translated by passing an object containing localised string values (there are only a few) โ see [Localisation](#localisation) |
| `customText` | `CustomTextDefinitions` | | In addition to [localising the component](#localisation) text strings, you can also *dynamically* alter them, depending on the data โ see [Custom Text](#dynamic-text--customtext) |
| `TextEditor` | `ReactComponent`
`
`
### External interaction
More detail [below](#programmatic-control)
| Prop | Type | Default | Description |
| ------------- | ----------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `onEditEvent` | `OnEditEventFunction` | - | Callback for the full edit lifecycle โ `start`/`submit`/`commit`/`cancel`, the instant `delete`/`move`, and the background `updateSuccess`/`updateError` settlement โ see [Event callbacks](#listening-to-the-lifecycle--oneditevent) |
| `onCollapse` | `OnCollapseFunction` | - | Callback to execute whenever the user collapses or opens a node |
| `editorRef` | `Ref
### Miscellaneous
| Prop | Type | Default | Description |
| ----------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | - | Name for the HTML `id` attribute on the main component container. |
| `className` | `string` | - | Name of a CSS class to apply to the overall component. In most cases, specifying `theme` properties will be more straightforward. |
To define an Enum, just add an object with the following structure to your "Types" array (either directly in the prop, or returned from the callback):
```js
{
enum: "My Enum Type" // name that will appear in the Types selector drop-down
values: [ // the list of allowed values
"Option A",
"Option B",
"Option C"
]
matchPriority: 1 // (Optional) used to recognize existing string values
// as the particular type (see below)
}
```
What is `matchPriority`? Well, when the data object is initialised, we have no way to know whether a given string value is "just a string" or is supposed to be one of the members of an Enum type (and we don't want to assume that if it's listed somewhere in an Enum `values` list that it definitely *should* be restricted to that type). So, if `matchPriority` is not defined, then that Enum type will *never* be initially assigned to a potentially matching Enum value when editing. If `matchPriority` is defined, then the highest priority Enum that has the value in its `values` list will be assigned (so if multiple Enums have overlapping `values`, the one with the highest priority will be applied.).
If the type of a given node is going to be *restricted* to a particular Enum type (i.e. the `allowTypeSelection` prop returns *only* one value), then a `matchPriority` is essential, otherwise it wouldn't be possible to switch a `string` to that type.
You can see examples of this in the [Star Wars data set](https://carlosnz.github.io/json-edit-react/?data=starWars) of the Demo โ the `eye_color`, `skin_color`, `hair_color` and `films` values are all restricted to a single, matching Enum type.
> [!NOTE]
> When editing, once an Enum type is selected from the Types selector, that node will continue to be displayed as that type for subsequent edits in the same session -- the `matchPriority` is purely for automatic recognition of a given value as a specific type when *first* editing it.
[](https://carlosnz.github.io/json-edit-react/examples/enums)
### New-key restrictions & default values
You can restrict the available properties a given collection node can have (when adding new properties) by setting the `newKeyOptions` prop. The value can be either a **list of keys**, or a [`NewKeyOptionsFunction`](#filter-functions) callback which returns the key list.
This will cause the UI to present a Drop-down selector when adding a new key rather than the usual text input:
The initial *value* for newly-added keys can also be defined with the `defaultValue` prop -- this can be *any* value, or a [`DefaultValueFunction`](#filter-functions) callback returning any value. The input signature is almost the same as standard `FilterFunctions`, but it can take a second argument, which is the name of the new key.
You can see an example of this in the [JSON Schema validation data](https://carlosnz.github.io/json-edit-react/?data=jsonSchemaValidation) of the Demo app when you add new keys to either the `address` collection or the root node.
[](https://carlosnz.github.io/json-edit-react/examples/new-key-restrictions)
### Drag-and-drop reordering
The `allowDrag` property controls which items (if any) can be dragged into new positions. By default, this is *off*, so you must set `allowDrag = true` to enable this functionality. Like the Edit permissions above, this property can also take a `FilterFunction` for fine-grained control. There are a couple of additional considerations, though:
- JavaScript does *not* guarantee object property order, so enabling this feature may yield unpredictable results. See [here](https://dev.to/frehner/the-order-of-js-object-keys-458d) for an explanation of how key ordering is handled.
> [!WARNING]
> It is strongly advised that you only enable drag-and-drop functionality if:
> 1. you're sure object keys will always be simple strings (i.e. not digits or non-standard characters)
> 2. you're saving the data in a serialisation format that preserves key order. For example, storing in a Postgres database using the `jsonb` (binary JSON) type, key order is meaningless, so the next time the object is loaded, the keys will be listed alphabetically.
- The `allowDrag` filter applies to the *source* node (the one being dragged) โ it controls whether a node can be **picked up** at all.
- What a drop is allowed to do then depends on where it lands, and reuses your existing permission filters โ so if you've configured restrictive editing constraints with Filter functions, they can't be circumvented via drag-n-drop:
| Drop | Is aโฆ | Requires |
| --- | --- | --- |
| Within the same collection | **reorder** | `allowEdit` on that collection |
| Into a *different* collection | **relocate** | `allowDelete` on the source **and** `allowAdd` on the destination collection |
A relocate is a delete-then-add across collections, so it needs both halves; a reorder changes nothing's membership, so it needs neither โ only that the collection itself is editable.
## Reacting to changes
### `onUpdate` โ accept, reject, transform
The **`onUpdate`** prop allows you to provide a callback that runs whenever the data changes in the editor โ for *every* kind of change. You might wish to use this to update some external state, post a notification, make an API call, modify the data before saving it, or [validate the data structure](#json-schema-validation) against a JSON schema. (It is *not* an alternative to `setData` โ see [Managing state](#managing-state).)
The function receives two arguments. The first is a single object, built on the standard [node data](#filter-functions) (`key`, `path`, `value`, `fullData`, etc.) with an **`event`** discriminant plus the change-specific fields; the second is a `control` object for [gating the commit](#async-updates--gating--hold):
```js
{
// ...standard node data (key, path, value, fullData, ...)
// describing the node BEFORE the change โ for `add`, this is the new
// node's position, with `value` unset
event, // 'edit' | 'add' | 'delete' | 'rename' | 'move'
newData, // the whole document AFTER the change
// ...plus one event-specific field:
newValue, // the new value (event: 'edit' | 'add')
newKey, // the new key (event: 'rename')
newPath, // the destination path (event: 'move')
}
```
Branch on `event` to handle each operation:
```js
onUpdate={(props) => {
switch (props.event) {
case 'edit': /* props.newValue */ break
case 'rename': /* props.newKey */ break
case 'move': /* props.newPath */ break
case 'add': /* props.newValue */ break
case 'delete': break
}
}}
```
The function can return nothing (the change proceeds as normal), or a value to accept/reject/transform/cancel the change. The return value can be one of:
- `true` / `void` / `undefined`: the change proceeds as normal
- `false`: treats the change as an error โ the data is not updated (reverts to the previous value) and a generic error message is displayed in the UI
- `null`: **silently cancels** the change โ no update, and *no* error message (unlike `false`). Use this to quietly abort a change that isn't an error
- `{ value: