Command Palette

Search for a command to run...

TreegeViewer

The TreegeViewer component renders a submitted flow as a read-only label / value recap — no inputs, no validation. With a flow, give it the submitted flowResponse (typically the onSubmit payload) and it replays them through the renderer's branch-visibility logic, so only the fields that were actually reachable for those values are shown. You can also use it without a flow — see Without a flow.

It resolves option values to their labels, formats dates/ranges and i18n labels exactly like the form, and excludes hidden/submit fields. Styling matches the renderer (shadcn/Tailwind, tg: prefix).

Import

import { TreegeViewer } from "treege/renderer"

Basic Usage

import { TreegeRenderer, TreegeViewer } from "treege/renderer"
import { useState } from "react"
import type { Flow, FormValues } from "treege"
 
function App({ flow }: { flow: Flow }) {
  const [submitted, setSubmitted] = useState<FormValues | null>(null)
 
  if (submitted) {
    return <TreegeViewer flow={flow} flowResponse={submitted} language="en" />
  }
 
  return <TreegeRenderer flow={flow} onSubmit={setSubmitted} />
}

Without a flow

When you only have stored, self-describing values (e.g. a persisted WorkflowValue[]) and no flow definition, omit flow and pass them as flowResponse. Each entry carries its own { name, type, value, label }, so values are still formatted by type (dates, ranges, booleans…). Option values are shown as-is (no flow to resolve their labels).

<TreegeViewer
  flowResponse={[
    { name: "city", type: "text", value: "Paris", label: { en: "City" } },
    { name: "dates", type: "daterange", value: "2026-06-01,2026-07-15", label: { en: "Dates" } },
  ]}
/>

Props

flow (optional)

The flow the values were submitted against. Omit it for flow-less mode.

flow?: Flow

flowResponse (required)

The submitted values. Its type is conditional on flow: FormValues (name/id-keyed, e.g. the onSubmit payload) when a flow is given, otherwise a self-describing FlowResponseEntry[].

flowResponse: FormValues | FlowResponseEntry[]

language (optional)

Language used to resolve translatable labels and options.

language?: string

Default: the TreegeRendererProvider config, then "en"

theme (optional)

Light/dark theme. Applies the tg: color variables so the viewer is self-styled even without a TreegeRenderer on the page.

theme?: "light" | "dark"

Default: the TreegeRendererProvider config, then "dark"

baseUrl (optional)

Base URL used to resolve relative file paths (e.g. uploads/x.png) into absolute URLs — same role as on TreegeRenderer / TreegeEditor. Absolute, data: and blob: URLs are left untouched.

baseUrl?: string

excludedFields (optional)

Field names (or ids) to hide from the view.

excludedFields?: string[]

excludeEmptyFields (optional)

Hide fields that have no submitted value (instead of showing emptyText). Useful to render a compact recap of only the filled-in fields.

excludeEmptyFields?: boolean

Default: false

collapsed (optional)

When true, only the first collapsedVisibleCount fields are rendered. Controlled — you own the toggle and state (see Collapse).

collapsed?: boolean

Default: false

collapsedVisibleCount (optional)

Number of fields kept visible while collapsed. Defaults to all (no effect unless set).

collapsedVisibleCount?: number

emptyText (optional)

Text shown when a field has no submitted value.

emptyText?: string

Default: "—"

className (optional)

Extra class names on the root element.

className?: string

renderField (optional)

Per-type rendering overrides for the value cell. Use this for app-specific cases — typically file (e.g. render thumbnails from your own storage) — while every other type keeps its built-in rendering.

renderField?: Partial<Record<InputType, (field: ViewerField) => ReactNode>>

renderRow (optional)

Wrap or replace a whole field row (label + value). Receives the resolved field and the default row node; return your own layout or the default.

renderRow?: (field: ViewerField, defaultRow: ReactNode) => ReactNode

Collapse

collapsed and collapsedVisibleCount are controlled — you render the toggle and own the state. While collapsed, only the first N fields show:

const [collapsed, setCollapsed] = useState(true)
 
return (
  <>
    <button onClick={() => setCollapsed((c) => !c)}>{collapsed ? "Show all" : "Show less"}</button>
    <TreegeViewer flow={flow} flowResponse={submitted} collapsed={collapsed} collapsedVisibleCount={3} />
  </>
)

Customizing rendering

Override the value cell for a specific input type with renderField, or take over the whole row layout with renderRow:

<TreegeViewer
  flow={flow}
  flowResponse={submitted}
  language="fr"
  excludedFields={["internalNote"]}
  renderField={{
    file: ({ rawValue }) => <Thumbnails files={rawValue} />,
  }}
/>

Headless usage

TreegeViewer is a thin layer over two field builders that return the ordered, display-ready fields. Use them directly when you want a fully custom layout (a table, a PDF export, columns…):

  • getViewerFields(flow, values, { language, baseUrl }) — flow-based resolution.
  • viewerFieldsFromResponse(response, { language }) — flow-less, from self-describing FlowResponseEntry[].
import { getViewerFields } from "treege/renderer"
 
const fields = getViewerFields(flow, values, { language: "en", baseUrl })
 
fields.forEach((field) => {
  field.label    // translated field label
  field.type     // input type (text, select, file, …)
  field.rawValue // the raw submitted value, untouched
  field.display  // normalized, render-ready value (see below)
})

ViewerField.display

The display property normalizes the value per input type so you can render it without re-deriving formatting rules:

display.kindShapeHow to render
text{ text: string }Plain text (also covers numbers, dates, ranges, resolved option labels)
boolean{ checked: boolean }Read-only toggle / checkbox (switch input)
tags{ tags: string[] }Chips (multi-select checkbox)
files{ files: SerializableFile[] }File names or thumbnails
emptyNo value was submitted for this field

Complete Example

<TreegeViewer
  flow={flow}
  flowResponse={submitted}
  language="en"
  baseUrl="https://cdn.example.com/"
  excludeEmptyFields
  emptyText="Not provided"
  renderField={{
    file: ({ rawValue }) => <Thumbnails files={rawValue} />,
  }}
/>