Data Display
JsonViewer
A JSON tree you can actually read: collapsible, searchable, and coloured with the same --code-* tokens as CodeBlock, so a payload in a docs page and a payload in a debug panel look like the same language.
import { JsonViewer } from 'omaris' Examples
Basic
Give it any value. depth decides how many levels start open — a collapsed node still says what is inside it rather than showing an opaque {…}.
<script lang="ts"> import { JsonViewer } from 'omaris'; const RESPONSE = { ok: true, order: { id: 'ord_8f21', total: 24500, currency: 'IQD', customer: { name: 'Lina Aziz', phone: '+964 750 000 0000', repeat: true }, items: [ { sku: 'lamb-quzi', qty: 1, price: 15000 }, { sku: 'tea', qty: 2, price: 1500 } ] }, delivered_at: null };</script><JsonViewer value={RESPONSE} depth={2} class="w-full" /> Search and variants
search filters the tree and keeps a match's ancestors, so a hit five levels down arrives with the path that leads to it. Try warn. The three variants are the same ones CodeBlock has.
<script lang="ts"> import { JsonViewer, SegmentedButton, type JsonViewerVariant } from 'omaris'; const CONFIG = { service: 'checkout', region: 'eu-west-1', logging: { level: 'warn', sinks: ['stdout', 'file'], sample: 0.25 }, limits: { rps: 200, burst: 400, timeout_ms: 3000 }, flags: { warn_on_retry: true, new_pricing: false } }; let variant = $state<JsonViewerVariant>('outlined');</script><div class="flex w-full flex-col gap-3"> <SegmentedButton label="Variant" size="sm" value={variant} onchange={(next) => (variant = next as JsonViewerVariant)} items={[ { value: 'outlined', label: 'Outlined' }, { value: 'filled', label: 'Filled' }, { value: 'ghost', label: 'Ghost' } ]} /> <JsonViewer value={CONFIG} name="config" depth={1} search {variant} height="14rem" /></div> Overridden
height is a custom property, not a class, so height="auto" lets the tree grow with its content and a max-h-* on classes.viewport really replaces it. indent tightens the levels and copy={false} drops the buttons.
<script lang="ts"> import { JsonViewer } from 'omaris'; const EVENT = { type: 'deploy.finished', build: 4821, duration_s: 96.4, commit: { sha: 'dafc5ae', author: 'omer', message: 'Ship the docs demos' } };</script><JsonViewer value={EVENT} name="event" depth={Infinity} copy={false} indent={22} height="auto" size="sm" variant="ghost" classes={{ root: 'rounded-shape-lg bg-surface-container-low', viewport: 'max-h-none py-2', key: 'font-semibold' }} class="w-full"/> When to use it
Use it for
- A payload someone will explore: an API response in a debug panel, a webhook's details, a row's raw record.
depth={2}opens the top, and a collapsed node still says what is inside:{ id, name, +3 },[ 12 items ]. - Finding one field in a large document.
searchkeeps the matches and the path down to them, so a hit five levels deep arrives with its ancestors. - Grabbing a piece.
copyputs a copy button on every node and a copy-all in the header. - A string that might be JSON. It is parsed when it is, and shown as a string when it is not.
Not for
- A short, fixed example nobody expands → Code Block with
lang="json". It copies and highlights with the same tokens. - Editing the document → Code Editor with
lang="json". - Structured log lines → Log Viewer. A row's
detailsobject already opens as this tree. - Data with a fixed shape people compare across rows → Table.
Do
- Name the root with
name, likeresponseorpayload, so the first row is not a bare brace. - Start shallow:
depth={1}for a document you do not know,Infinityonly for something small. The previews make skipping a node possible. - Give it a
heightwhen it sits in a panel and let the tree scroll inside. Useheight="auto"when the card around it scrolls. - Use
variant="ghost"inside a bordered surface. The three variants match Code Block.
Don't
- Stringify first. Pass the object; a pre-stringified value loses the types, and the colours are the types.
- Turn
searchon for a ten-key object. The field is taller than the tree. - Set the height from a class alone.
heightis a property, so amax-h-*onclasses.viewportonly works onceheight="auto"has cleared it.
Quick reference
variant outlined(default)filledghost
size smmd(default)
API
JsonViewer
A JSON tree you can actually read: collapsible, searchable, and coloured with the same --code-* tokens as CodeBlock, so a payload in a docs page and a payload in a debug panel look like the same language.
A collapsed node still tells you what is inside it — { id, name, +3 }, [ 12 items ] — because the usual alternative, an opaque {…}, means expanding every node just to find the one you wanted.
Filtering keeps matches and their ancestors, so a hit five levels down arrives with the path that leads to it rather than as an orphan row.
import { JsonViewer } from 'omaris' <JsonViewer value={response} depth={2} search copy /> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- No description in the source yet.
header- Filter box and the toolbar buttons.
search- No description in the source yet.
action- No description in the source yet.
viewport- The scrolling tree.
row- One row: the toggle, the key, and either a value or a brace.
toggle- The disclosure triangle. A leaf gets an empty one, to keep the column.
spacer- No description in the source yet.
key- No description in the source yet.
punctuation- No description in the source yet.
value- A scalar. The colour says which type it is.
preview- The summary drawn inside a collapsed brace.
count12 items, at the end of a container row.copy- Copy-this-node, revealed on hover.
empty- Nothing matched the filter.
Props
value required unknown Any value. A string that parses as JSON is parsed; one that doesn't is shown as a string.
name Defaults to '$'
string Name for the root node.
variant Defaults to 'outlined'
JsonViewerVariant outlinedfilledghost
size Defaults to 'md'
JsonViewerSize smmd
depth Defaults to 1
number How many levels start open. Infinity opens everything.
search Defaults to false
boolean Show the filter box.
copy Defaults to true
boolean Per-node copy buttons, and a copy-all in the header.
maxStringLength Defaults to 120
number Longest string shown before it is elided.
height Defaults to '20rem'
number | string Height of the scroll area. A number is px, a string any CSS length — a property rather than a class, so classes.viewport can replace it.
indent Defaults to 14
number Indent per level, in px.
class string classes JsonViewerClasses