Display

Schema Display

An accessible API operation viewer with parameters, request and response schemas, recursive fields, depth guards, and four fetch states.

Preview in your theme

Loading preview…

"use client"

import * as React from "react"
import { AlertCircle, Braces, ChevronRight, RefreshCw } from "lucide-react"
import { cn } from "@/lib/utils"
import type { HttpMethod, SchemaDisplayData, SchemaDisplayNode } from "./schema-display.contract"

export type SchemaDisplayProps = SchemaDisplayData &
  Omit<React.HTMLAttributes<HTMLElement>, "children"> & {
    defaultExpandedDepth?: number
    maxDepth?: number
    onRetry?: () => void
  }

Installation

npx shadcn@latest add https://ui.zyeon.ai/r/schema-display.json

Prompt

Build a React + TypeScript + Tailwind "SchemaDisplay" component with
lucide-react and zod.

Contract
- Make one zod discriminated union the data source:
  { status: "loading"; message } |
  { status: "empty"; message } |
  { status: "error"; message } |
  { status: "ready"; operation }.
- operation contains method (GET / POST / PUT / PATCH / DELETE / HEAD /
  OPTIONS), path, summary, optional description, parameters, an optional
  request body, and responses.
- A parameter has id, name, location (path / query / header / cookie), type,
  optional description, and optional required.
- Request and response bodies declare mediaType and schema fields. A response
  also declares its HTTP status and description.
- Anchor the recursive field type with an explicit SchemaDisplayNode interface,
  then validate it with z.ZodType<SchemaDisplayNode> + z.lazy(). Each node has
  id, name, type, optional description / required / nullable / format /
  enumValues, and a required children array; children: [] is a leaf. Keep each
  id stable and unique among its siblings because the tree builds identity from
  the ancestor-id path rather than array positions.
- Export SchemaDisplay, SchemaDisplaySection, and SchemaDisplayTree. Main props
  extend the zod-derived state union plus native article attributes,
  defaultExpandedDepth?: number (default 2), maxDepth?: number (default 6),
  and onRetry?: () => void.

Behavior
- Treat loading, empty, error, and ready as first-class branches. Loading uses
  an anatomy-matching skeleton and aria-busy; empty is a status message; error
  is an alert and only renders Try again when onRetry exists.
- In ready state, render a safe text-tokenized method/path header (never
  dangerouslySetInnerHTML), summary, parameters, optional request body, and
  every response with status and media type. Empty section arrays get honest
  "No … defined" copy rather than disappearing.
- Disclosure headings are native buttons with aria-expanded and aria-controls.
  They start open and preserve their own state without a controllable-state
  dependency.
- SchemaDisplayTree iteratively projects only visible nodes. It uses role=tree,
  role=treeitem, aria-level / posinset / setsize, and one roving tab stop.
  Arrow Up/Down move rows; Right expands or enters a child; Left collapses or
  returns to the parent; Home/End jump; Enter/Space toggle.
- Build row keys from encoded ancestor ids. When the node set or maxDepth
  changes, remove stale expansion keys and move a missing focus key to the
  first remaining row; inserting/reordering siblings must not move state to a
  different field.
- Clamp maxDepth to 1…12 and defaultExpandedDepth to 0…maxDepth. Use iterative
  stacks for indexing, initial expansion, and visible-row projection. A branch
  at the depth ceiling stays visible and reports how many nested fields were
  hidden. An empty schema renders "No fields defined."

Rendering & styling
- Use semantic tokens only: bg-card, bg-muted, bg-primary/10, text-primary,
  text-muted-foreground, bg-destructive/10, text-destructive, border, and ring.
  Merge every public className with cn().
- Let field names, types, enum values, section metadata, and response media
  types wrap or break inside the panel on narrow screens; do not turn a whole
  row into max-content. Keep only code-like endpoint paths locally breakable.
- Use a focus-visible ring on disclosure, retry buttons, and the currently
  keyboard-focused treeitem; roving state controls tabIndex but must not leave
  a permanent ring on the last active row. Chevron transitions become immediate under
  prefers-reduced-motion.

Customization levers
- Density: change section and row padding together; keep at least a 32px row
  target and preserve the roving-focus ring.
- Information depth: adjust defaultExpandedDepth for first-paint scanning and
  maxDepth for defensive rendering; do not remove the visible depth-limit note.
- Field metadata: omit enumValues / format / nullable upstream for a quieter
  view, or add constrained badges beside the existing type badge.
- Method emphasis: remap METHOD_CLASS to the host's semantic token policy while
  keeping method text visible so color is never the only signal.
- Composition: use SchemaDisplaySection and SchemaDisplayTree independently
  inside an endpoint drawer or split-pane layout; keep the same contract and
  keyboard model.

Concepts

  • Stable identity path — ancestor ids, not sibling positions, carry expansion and focus across insertions or reordering; missing ids are pruned when data changes.
  • Visible-row projection — keyboard movement operates on the iteratively flattened rows readers can currently see, so collapsed descendants never become invisible focus targets.
  • Bounded recursion — recursive contracts can be arbitrarily deep; a clamped maxDepth and iterative traversal convert that risk into an explicit terminal row with a hidden-child count.
  • Safe path tokenization{projectId} segments are highlighted as React text spans, preserving untrusted path text without injecting HTML.
  • Disclosure before detail — parameters, request, and responses start open but remain independently collapsible, letting readers reduce density without losing endpoint context.
  • Contract-driven four states — network lifecycle and operation data share one discriminated union, which prevents a partial ready payload from masquerading as a complete schema.

On This Page