MonacoJsonInput

Form input for JSON values powered by the Monaco editor (the same editor that powers VS Code). Supports JSON Schema validation, syntax highlighting, error markers, and auto-grow.

Monaco is heavy (~600 KB gzipped plus web workers), so this component is lazy-loaded behind a Suspense boundary. The chunk loads on first mount and is reused for subsequent mounts.

Usage

import { Edit, SimpleForm } from "@/components/admin";
import { MonacoJsonInput } from "@/components/monaco";
 
const ProductEdit = () => (
  <Edit>
    <SimpleForm>
      <MonacoJsonInput
        source="metadata"
        schema={{
          type: "object",
          properties: {
            sku: { type: "string" },
            price: { type: "number" },
          },
          required: ["sku", "price"],
        }}
        autoHeight
      />
    </SimpleForm>
  </Edit>
);

Value-shape detection

MonacoJsonInput auto-detects whether the record's value is a string (raw JSON text) or an object (already parsed) on the first non-undefined value it sees, and locks that shape for the rest of the input's lifetime.

  • Object mode (default for objects, arrays, null, numbers, booleans): the editor displays JSON.stringify(value, null, 2). On change, the editor text is parsed and the parsed value committed. Invalid JSON is not committed — the last good value remains, and the marker drives the form error.
  • String mode (when the initial value is a string): the editor text is committed verbatim every keystroke. Validation is non-blocking.

Props

PropRequiredTypeDefaultDescription
sourceRequiredstring-Field name
schemaOptionalobject-JSON Schema; editor content is validated against this schema
schemaUriOptionalstring-Remote schema URL (Monaco fetches it)
allowCommentsOptionalbooleanfalseAllow JSONC syntax (comments + trailing commas)
autoHeightOptionalbooleanfalseGrow the editor with content
heightOptionalnumber | string300Fixed height (only when autoHeight=false)
minHeightOptionalnumber | string120Lower bound for autoHeight
maxHeightOptionalnumber | string600Upper bound for autoHeight
showFormatButtonOptionalbooleantrueShow a "Format" button overlay
readOnlyOptionalbooleanfalseDisable editing
classNameOptionalstring-Classes on the wrapping <FormField>
editorClassNameOptionalstring-Classes on the editor's bordered wrapper
monacoOptionsOptionaleditor.IStandaloneEditorConstructionOptions-Escape hatch — merged into Monaco's options
validateOptionalValidator | Validator[]-Composed AFTER the built-in schema validator
labelOptionalstring | falseInferredCustom or hidden label
helperTextOptionalReactNode-Help text under the editor
defaultValueOptionalunknown-Default value if the record's field is undefined

Schema validation

When you pass schema, the editor uses Monaco's built-in JSON Schema diagnostics. Schema violations appear as red gutter markers AND as a form-level error. The form will refuse to submit while any marker has Error severity.

The built-in schema validator is composed with your validate prop using composeValidators from ra-core. The schema validator runs first; if it returns an error, that wins. Otherwise, your custom validators run.

In string mode, schema validation is skipped entirely (the editor content is treated as opaque text).

Suspense and lazy-loading

This component wraps its heavy inner in a React.Suspense boundary with a skeleton fallback. You don't need to add your own Suspense — but if your page is already in a React.lazy() boundary, that's fine; React handles nested boundaries.

The Monaco chunk is loaded on first mount of any MonacoJsonInput or MonacoJsonField and reused for the rest of the session.

Tips

  • Don't use this in List cells — it would spin up one Monaco instance per row. Use <JsonField> instead.
  • For very large schemas, prefer schemaUri so Monaco caches the fetch.
  • The format button runs Monaco's built-in editor.action.formatDocument (Cmd/Ctrl+Shift+I).