Skip to content

JSON Schema examples

Each example pairs the schema shape with a live JsonSchemaForm Storybook demo. Edit the rendered controls to see how nested values, arrays, maps, and conditionals behave.

All examples use the same controlled React pattern. JSON Schema default values are placeholders; initialize value explicitly when a default must already exist in submitted state.

import { useState } from "react";
import {
JsonSchemaForm,
type JsonSchemaObject,
} from "@flanksource/clicky-ui/components";
export function Example({ schema }: { schema: JsonSchemaObject }) {
const [value, setValue] = useState<Record<string, unknown>>({});
return (
<>
<JsonSchemaForm schema={schema} value={value} onChange={setValue} />
<pre>{JSON.stringify(value, null, 2)}</pre>
</>
);
}

Primitive properties map to their natural controls. A string renders as text, numbers use numeric behavior, booleans use a switch, enums use a combobox, and a plain string array uses the compact tag editor.

{
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string", "title": "Full name" },
"age": { "type": "integer", "title": "Age", "minimum": 0 },
"active": { "type": "boolean", "title": "Active" },
"role": {
"type": "string",
"title": "Role",
"enum": ["admin", "editor", "viewer"]
},
"tags": {
"type": "array",
"title": "Tags",
"items": { "type": "string" }
}
}
}
Rendered form · Open in Storybook

String format selects a specialized editor. Supported formats include date, date-time, textarea, percent, and md. Markdown fields load MDXEditor lazily and accept typed x-md-editor options.

{
"type": "object",
"properties": {
"publishedAt": {
"type": "string",
"format": "date-time",
"title": "Published at"
},
"summary": {
"type": "string",
"format": "textarea",
"title": "Summary"
},
"body": {
"type": "string",
"format": "md",
"title": "Body",
"x-md-editor": {
"admonitions": true,
"frontmatter": true,
"tables": true
}
}
}
}
Rendered markdown field · Open in Storybook

The rendered demo focuses on format: "md". Date fields use the date picker, percent keeps the stored value numeric while displaying a percent suffix, and textarea uses a multiline text control. See the MdxEditorField API for the standalone editor surface.

Objects with fixed properties recurse into headed sub-forms. Required markers and edits remain scoped to each nested object while onChange receives the rebuilt root value.

{
"type": "object",
"properties": {
"name": { "type": "string", "title": "Service name" },
"database": {
"type": "object",
"title": "Database",
"required": ["host"],
"properties": {
"host": { "type": "string", "title": "Host" },
"port": { "type": "integer", "title": "Port" },
"credentials": {
"type": "object",
"title": "Credentials",
"properties": {
"user": { "type": "string", "title": "User" },
"password": { "type": "string", "title": "Password" }
}
}
}
}
}
}
Rendered nested objects · Open in Storybook

When items is an object schema, the array opens by its visible column count: up to x-table-max-columns (default 4) scalar columns render as a grid, and wider items — or items holding a nested object or object list — render as accordion rows with one item open at a time and its fields labelled inline. A ⋮ menu on the array’s summary line switches between Grid, Stack form and Inline form over the same value. x-layout: "table" and x-array-display: "accordion" pick the opening view explicitly. Use x-array-display: "cards" to keep titled cards open, or "stacked" for full sub-forms. The x-item extension supplies item titles, summaries, and available actions.

{
"type": "object",
"properties": {
"servers": {
"type": "array",
"title": "Servers",
"items": {
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string", "title": "Name" },
"port": { "type": "integer", "title": "Port", "minimum": 0 },
"tls": { "type": "boolean", "title": "TLS" }
}
}
}
}
}
Rendered object array · Open in Storybook

An object with a schema-valued additionalProperties renders editable key/value rows. Declared properties can still have stronger controls, while extra keys use the additional-property schema.

{
"type": "object",
"properties": {
"labels": {
"type": "object",
"title": "Labels",
"properties": {
"environment": {
"type": "string",
"enum": ["dev", "staging", "prod"]
}
},
"additionalProperties": { "type": "string" }
}
}
}
Rendered string map · Open in Storybook

The control can add, rename, and remove extra keys. Declared properties keep their schema-derived input, while every undeclared key uses additionalProperties.

if and then branches add fields according to the current value. Switching the controlling enum immediately changes the effective property set.

{
"type": "object",
"properties": {
"notify": {
"type": "string",
"title": "Notify via",
"enum": ["none", "email", "webhook"]
}
},
"allOf": [
{
"if": {
"properties": { "notify": { "const": "email" } },
"required": ["notify"]
},
"then": {
"required": ["address"],
"properties": {
"address": { "type": "string", "title": "Email address" }
}
}
},
{
"if": {
"properties": { "notify": { "const": "webhook" } },
"required": ["notify"]
},
"then": {
"required": ["url"],
"properties": {
"url": { "type": "string", "title": "Webhook URL" }
}
}
}
]
}
Rendered conditional form · Open in Storybook

The renderer evaluates if/then clauses inside allOf. Hiding a branch does not delete its existing value; normalize the object in the host if inactive branch data must be removed before submission.

Clicky UI’s x-* keywords change presentation without changing the stored value. This example combines segmented enums, icons, descriptions, a 12-column grid, spans, and input adornments.

{
"type": "object",
"x-columns": 12,
"properties": {
"runtime": {
"type": "string",
"title": "Runtime",
"enum": ["claude", "codex"],
"x-enum-display": "segmented",
"x-enum-icons": {
"claude": "anthropic",
"codex": "openai"
},
"x-enum-descriptions": {
"claude": "Claude Code runtime",
"codex": "Codex runtime"
},
"x-col-span": 12
},
"model": {
"type": "string",
"title": "Model",
"x-input-prefix-icon": "sparkles",
"x-col-span": 8
},
"temperature": {
"type": "number",
"title": "Temperature",
"minimum": 0,
"maximum": 2,
"x-col-span": 4
}
}
}
Rendered presentation extensions · Open in Storybook

For the complete keyword list, see JSON Schema extensions.

Use property mode for a detail view whose values can be edited individually. The following complete component starts with a populated service, expands its nested connection into aligned rows, and keeps the identifier read-only.

import { useState } from "react";
import {
JsonSchemaForm,
type JsonSchemaObject,
} from "@flanksource/clicky-ui/components";
const schema: JsonSchemaObject = {
type: "object",
required: ["name"],
properties: {
name: { type: "string", title: "Name" },
enabled: { type: "boolean", title: "Enabled" },
connection: {
type: "object",
title: "Connection",
properties: {
host: { type: "string", title: "Host" },
retries: { type: "integer", title: "Retries", minimum: 0 },
},
},
identifier: { type: "string", title: "Identifier", readOnly: true },
},
};
export function ServiceProperties() {
const [value, setValue] = useState<Record<string, unknown>>({
name: "Example service",
enabled: true,
connection: { host: "localhost", retries: 3 },
identifier: "service-01",
});
return (
<>
<JsonSchemaForm
schema={schema}
value={value}
onChange={setValue}
layout={{ mode: "properties" }}
showPreferencesMenu={false}
/>
<pre>{JSON.stringify(value, null, 2)}</pre>
</>
);
}

Click Name, change its text, and cancel: the preview keeps the original value. Edit it again and save to commit the draft. Repeat with Connection → Host to check a nested value; Identifier has no edit button. The Storybook demo extends this example with credentials, an enum, and tags.

Editable property rows · Open in Storybook