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" } },
]}
/>flowResponse is typed conditionally on flow: with a flow it's FormValues, without a flow it's FlowResponseEntry[].
Props
flow (optional)
The flow the values were submitted against. Omit it for flow-less mode.
flow?: FlowflowResponse (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?: stringDefault: 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?: stringexcludedFields (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?: booleanDefault: false
collapsed (optional)
When true, only the first collapsedVisibleCount fields are rendered. Controlled — you own the toggle and state (see Collapse).
collapsed?: booleanDefault: false
collapsedVisibleCount (optional)
Number of fields kept visible while collapsed. Defaults to all (no effect unless set).
collapsedVisibleCount?: numberemptyText (optional)
Text shown when a field has no submitted value.
emptyText?: stringDefault: "—"
className (optional)
Extra class names on the root element.
className?: stringrenderField (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) => ReactNodeCollapse
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-describingFlowResponseEntry[].
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.kind | Shape | How 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 |
empty | — | No 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} />,
}}
/>