Skip to content

shadcn.io is an independent project built on shadcn/ui

Components

Free

Schema Display for Radix UI

An endpoint's method, path and parameters, with request and response fields that open. For React with Radix UI, for the APIs your agent's tools call.

An endpoint on one card: its method and path, what it does, its parameters, and the fields it takes and returns.

Use it to document the HTTP API behind a tool, a webhook your agent receives, or an endpoint it is about to call. Each {param} in the path is highlighted, and an object or array field opens into its own fields. To show a call that already ran, with its input and output, use Tool. In React, Radix UI runs each section as a collapsible, so the parts open from the keyboard.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

The install adds Badge for the method and the field types, and Collapsible for the sections that open.

Anatomy

Schema Display anatomy: root, method, path, parameters, request, property
SchemaDisplay
├── SchemaDisplayHeader
│   ├── SchemaDisplayMethod
│   └── SchemaDisplayPath
├── SchemaDisplayDescription
├── SchemaDisplayContent
│   ├── SchemaDisplayParameters
│   │   └── SchemaDisplayParameter (one per parameter)
│   ├── SchemaDisplayRequest
│   │   └── SchemaDisplayBody
│   │       └── SchemaDisplayProperty (opens when it has fields)
│   └── SchemaDisplayResponse
│       └── SchemaDisplayBody
│           └── SchemaDisplayProperty
└── SchemaDisplayExample

With no children, SchemaDisplay renders the header, the description and a section for each list it was given. Pass children to lay the parts out yourself: they read the method, the path and the lists from the root.

Usage

import { SchemaDisplay } from "@/components/ui/schema-display";
<SchemaDisplay
  description="Send a message to a specific user"
  method="POST"
  parameters={[
    { location: "path", name: "userId", required: true, type: "string" },
  ]}
  path="/api/users/{userId}/messages"
  requestBody={[{ name: "content", required: true, type: "string" }]}
  responseBody={[{ name: "id", type: "string" }]}
/>

Accessibility

Tab
Moves focus to the next section or field that opens.
Enter
Opens or closes the focused section or field.
Space
Opens or closes the focused section or field.
  • Each section and each field with fields of its own is a button with aria-expanded. A field's description sits outside its button, so the button's name stays short.
  • The method is written out, not only colored, and required is a word, so neither relies on color.
  • Paths, field names, types and the example always read left to right. The card follows the page direction, and the chevrons point the reading way.
  • The example scrolls inside its block and can take focus. Give it an aria-label, such as Example response.

Demos

Nested

An order with a customer, an address inside the customer, and a list of items. A field with properties opens into them, and a field with items opens into one entry named after it with []. The first two levels start open and deeper ones start closed, so a large body still fits on screen. Each level steps in by 1rem.

Preview width

Methods

Five endpoints of one resource, one per method, each a header with its description on the end. GET is green, POST blue, PUT and PATCH amber and DELETE red, from Badge's status variants, so they follow your theme. A card with only a header is an index: link each row to its full card. Give the method a fixed width so the paths line up.

Preview width

Composed

The parts laid out by hand: Parameters starts closed with its count in view, there is no request body, and an example response sits under the fields. SchemaDisplayExample is a pre for any text. Compose the parts when the default order doesn't fit, or to add something of your own between them.

Preview width

From JSON Schema

A tool's input is JSON Schema, and so is an OpenAPI body. Turn its properties into the fields Schema Display takes, and show the tool's input as a request body:

type JsonSchema = {
  description?: string;
  items?: JsonSchema;
  properties?: Record<string, JsonSchema>;
  required?: string[];
  type?: string;
};

const toProperties = (schema: JsonSchema): SchemaProperty[] =>
  Object.entries(schema.properties ?? {}).map(([name, field]) => ({
    description: field.description,
    items: field.items
      ? {
          name: "item",
          properties: field.items.properties
            ? toProperties(field.items)
            : undefined,
          type: field.items.type ?? "unknown",
        }
      : undefined,
    name,
    properties: field.properties ? toProperties(field) : undefined,
    required: schema.required?.includes(name),
    type: field.type ?? "unknown",
  }));

SchemaProperty and SchemaParameter are exported from the schema display file.

Migrating from AI SDK Elements

If you added Schema Display from AI SDK Elements (opens in a new tab), your project has components/ai-elements/schema-display.tsx. Install Schema Display from this page, change the imports from @/components/ai-elements/schema-display to @/components/ui/schema-display, and delete the old file. The parts keep their names and props. What changes:

  • SchemaDisplayPath renders {params} as elements, without dangerouslySetInnerHTML. Children passed to it render as they are, never as HTML.
  • The method colors are Badge's success, info, warning and destructive variants instead of fixed green, blue, orange and red, so they follow your theme.
  • SchemaDisplayRequest and SchemaDisplayResponse put a rule between fields, as nested fields already had.

FAQ

Which HTTP methods does Schema Display color?

GET, POST, PUT, PATCH and DELETE, each a Badge variant: GET success, POST info, PUT and PATCH warning, DELETE destructive. The colors come from your theme's status tokens, so they hold in dark mode. The badge also sets data-method, and children passed to SchemaDisplayMethod replace the method's text.

How do I show path, query and header parameters in React?

Pass parameters to SchemaDisplay: each has a name, a type, and optionally location (path, query or header), required and a description. They show under Parameters with a count, each with its type and location as badges. Parameters is open at first; close it from its own props when the list is long.

How do nested objects and arrays show?

A field with properties opens into its fields, and a field with items opens into one entry named after the field with [] on the end. Levels step in by 1rem. The first two levels start open and deeper ones start closed, so a large body still fits on the page.

How are path parameters highlighted?

Each parameter in the path, a name in curly braces, is wrapped in a span in the primary color, with data-slot schema-display-path-param, so you can restyle it. The path reads left to right on a right-to-left page and wraps anywhere when it's too long for the card. Pass children to SchemaDisplayPath to render your own markup instead.

Can I lay out the parts myself?

Yes. Give SchemaDisplay children and it renders only those, while every part still reads the method, path, description and lists from the root. Leave a section out, close one at first, or add a SchemaDisplayExample under the fields, as the Composed demo does.

How do I style a Schema Display with Radix UI?

Edit the schema display file under components/ui. Every part has its own data-slot, and each field sets data-depth, so a style can target one level. The sections are your Collapsible and the badges your Badge, so their styles come from those files. Pass className to any part for a one-off change.

How do I install Schema Display in React?

Run the command in the Installation section on this page. It adds the schema display file, Badge and Collapsible. Import SchemaDisplay and the parts you compose from that file, with the SchemaParameter and SchemaProperty types for your data. The command uses the shadcn CLI.

API Reference

SchemaDisplay

PropType
methodHttpMethod

The HTTP method: its badge's color.

pathstring

The path; each {param} in it is highlighted.

description?string

What the endpoint does, under its header.

parameters?SchemaParameter[]

Its path, query and header parameters.

requestBody?SchemaProperty[]

The request body's fields.

responseBody?SchemaProperty[]

The response body's fields.

className?string

Merged with the part's classes.

SchemaDisplayHeader

<div> data-slot="schema-display-header"

PropType
className?string

Merged with the part's classes.

SchemaDisplayMethod

data-slot="schema-display-method"

PropType
pill?boolean

Fully rounded corners.

size?"lg" | "md" | "sm"

The size.

Type
"lg" | "md" | "sm"
variant?"default" | "destructive" | "ghost" | "info" | "link" | "outline" | "secondary" | "success" | "warning"

The visual style.

Type
"default" | "destructive" | "ghost" | "info" | "link" | "outline" | "secondary" | "success" | "warning"
Default
"default"
asChild?boolean

Merge props onto the child element instead of rendering a host.

className?string

Merged with the part's classes.

AttributeType
data-method?string

Mirrors method.

SchemaDisplayPath

<span> data-slot="schema-display-path"

PropType
className?string

Merged with the part's classes.

SchemaDisplayDescription

<p> data-slot="schema-display-description"

PropType
className?string

Merged with the part's classes.

SchemaDisplayContent

<div> data-slot="schema-display-content"

PropType
className?string

Merged with the part's classes.

SchemaDisplayParameters

data-slot="schema-display-parameters"

PropType
open?boolean

Whether it's open. Pair with onOpenChange to control it.

defaultOpen?boolean

Whether it starts open, when you don't control open.

Default
true
onOpenChange?{ bivarianceHack(open: boolean): void; }["bivarianceHack"]

Called when it opens or closes.

Type
{ bivarianceHack(open: boolean): void; }["bivarianceHack"]
disabled?boolean

Whether the parameters is disabled.

asChild?boolean

Merge props onto the child element instead of rendering a host.

className?string

Merged with the part's classes.

SchemaDisplayParameter

<div> data-slot="schema-display-parameter"

PropType
namestring

The parameter's name, as the request spells it.

typestring

Its type, shown as a badge (string, integer, boolean…).

description?string

What the parameter does, under its name.

location?"path" | "query" | "header"

Where the request carries it: in the path, the query string or a header.

Type
"path" | "query" | "header"
required?boolean

Marks it required; the rest are optional.

className?string

Merged with the part's classes.

SchemaDisplayRequest

data-slot="schema-display-request"

PropType
open?boolean

Whether it's open. Pair with onOpenChange to control it.

defaultOpen?boolean

Whether it starts open, when you don't control open.

Default
true
onOpenChange?{ bivarianceHack(open: boolean): void; }["bivarianceHack"]

Called when it opens or closes.

Type
{ bivarianceHack(open: boolean): void; }["bivarianceHack"]
disabled?boolean

Whether the request is disabled.

asChild?boolean

Merge props onto the child element instead of rendering a host.

className?string

Merged with the part's classes.

SchemaDisplayResponse

data-slot="schema-display-response"

PropType
open?boolean

Whether it's open. Pair with onOpenChange to control it.

defaultOpen?boolean

Whether it starts open, when you don't control open.

Default
true
onOpenChange?{ bivarianceHack(open: boolean): void; }["bivarianceHack"]

Called when it opens or closes.

Type
{ bivarianceHack(open: boolean): void; }["bivarianceHack"]
disabled?boolean

Whether the response is disabled.

asChild?boolean

Merge props onto the child element instead of rendering a host.

className?string

Merged with the part's classes.

SchemaDisplayBody

<div> data-slot="schema-display-body"

PropType
className?string

Merged with the part's classes.

SchemaDisplayProperty

<div> data-slot="schema-display-property"

PropType
namestring

The field's key in the body.

typestring

Its type, shown as a badge (string, object, array…).

depth?number

How deep in the body it is: each level steps in 1rem.

Default
0
description?string

What the field holds, under its name.

items?object

For an array: the shape of each item, which opens under the field.

Type
{ description?: string; items?: SchemaProperty; name: string; properties?: SchemaProperty[]; required?: boolean; type: string }
properties?SchemaProperty[]

For an object: its fields, which open under it.

required?boolean

Marks it required; the rest are optional.

className?string

Merged with the part's classes.

AttributeType
data-depth?string

Mirrors depth.

SchemaDisplayExample

<pre> data-slot="schema-display-example"

PropType
className?string

Merged with the part's classes.