MKSingh

MKSingh

Docs
JSONForm Renderers

Layouts

Layout renderers that control how form elements are arranged — vertical stacks, horizontal rows, accordion groups, tabs, and dynamic arrays.

Layouts are UI Schema elements that contain other elements. They have no direct connection to schema properties — they only control visual structure.

VerticalLayout

Stacks elements in a column with flex flex-col gap-2. This is the default root layout for most forms.

npx shadcn@latest add https://mksingh.dev/r/jsonforms-vertical-layout.json
UISchema
{
    "type": "VerticalLayout",
    "elements": [
        { "type": "Control", "scope": "#/properties/firstName" },
        { "type": "Control", "scope": "#/properties/lastName" }
    ]
}

HorizontalLayout

Arranges elements in a row with equal widths (flex-1 per child). Useful for side-by-side fields.

npx shadcn@latest add https://mksingh.dev/r/jsonforms-horizontal-layout.json
UISchema
{
    "type": "HorizontalLayout",
    "elements": [
        { "type": "Control", "scope": "#/properties/firstName" },
        { "type": "Control", "scope": "#/properties/lastName" }
    ]
}

Group

Wraps a set of fields in a collapsible ShadCN Accordion. The label property becomes the accordion trigger text. Defaults to open.

npx shadcn@latest add https://mksingh.dev/r/jsonforms-group.json
UISchema
{
    "type": "Group",
    "label": "Contact Details",
    "elements": [
        { "type": "Control", "scope": "#/properties/email" },
        { "type": "Control", "scope": "#/properties/phone" }
    ]
}

Categorization (Tabs)

Renders a set of categories as ShadCN Tabs. Each category becomes a tab, and its elements render inside the tab panel. Good for multi-section forms.

npx shadcn@latest add https://mksingh.dev/r/jsonforms-categorization.json
UISchema
{
    "type": "Categorization",
    "elements": [
        {
            "type": "Category",
            "label": "Personal",
            "elements": [
                { "type": "Control", "scope": "#/properties/firstName" }
            ]
        },
        {
            "type": "Category",
            "label": "Address",
            "elements": [
                { "type": "Control", "scope": "#/properties/street" }
            ]
        }
    ]
}

Label

Renders a static piece of text inside a layout. Useful for headings or instructions inside the form.

npx shadcn@latest add https://mksingh.dev/r/jsonforms-label.json
UISchema
{
    "type": "Label",
    "text": "Fill in your details below"
}

Array Layout

Renders arrays of objects as a list of collapsible accordion panels. Each item gets:

  • Its own expand panel numbered from 1
  • Full form rendered via JsonFormsDispatch
  • Up / Down buttons to reorder
  • Remove button to delete

Triggered automatically on array schemas where the items are objects (isObjectArrayWithNesting).

npx shadcn@latest add https://mksingh.dev/r/jsonforms-array-layout.json
Schema
{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "name":  { "type": "string" },
            "email": { "type": "string", "format": "email" }
        }
    }
}
UISchema
{
    "type": "Control",
    "scope": "#/properties/contacts",
    "label": "Contact"
}

The array layout uses findUISchema to auto-generate the inner form layout if you don't provide one. You can override it by registering a custom UI Schema in the uischemas prop of <JsonForms />.

Disabling add/remove

Pass enabled={false} at the <JsonForms /> level or use JSONForms' rule system to disable the array control — the add and action buttons are gated on the enabled prop.


Nesting layouts

All layouts compose naturally. A common pattern for a real-world form:

UISchema
{
    "type": "VerticalLayout",
    "elements": [
        { "type": "Label", "text": "New User" },
        {
            "type": "HorizontalLayout",
            "elements": [
                { "type": "Control", "scope": "#/properties/firstName" },
                { "type": "Control", "scope": "#/properties/lastName" }
            ]
        },
        {
            "type": "Group",
            "label": "Address",
            "elements": [
                { "type": "Control", "scope": "#/properties/street" },
                { "type": "Control", "scope": "#/properties/city" }
            ]
        }
    ]
}

On this page