Visualizations & Layouts
Visualizations & Layouts
Built-in visualization types
Add a visualizations array to your schema to render charts and tables below the results section.
progress
A single horizontal progress bar with optional label and value display.
visualizations: [
{
type: 'progress',
label: 'Progress to Break-Even',
value: ({ fields, derived }) =>
Math.min(100, (fields.expectedUnits / derived.breakEvenUnits) * 100),
max: 100,
color: ({ derived }) => derived.profitAtForecast > 0 ? 'success' : 'warning',
showValue: true,
}
]
benchmark
A progress bar with a benchmark marker line — useful for showing where you stand vs. an industry standard.
{
type: 'benchmark',
label: 'LTV:CAC vs Industry Benchmark',
value: ({ derived }) => derived.ltvCacRatio,
benchmark: 3, // show a marker at 3.0
max: 10,
format: 'number',
}
bar-chart
Vertical or horizontal bar chart from a list of { label, value } items.
{
type: 'bar-chart',
label: 'Revenue by Plan',
orientation: 'vertical', // 'vertical' | 'horizontal'
items: ({ fields, derived }) =>
(derived.planBreakdown ?? []).map(p => ({
label: p.name,
value: p.revenue,
})),
format: 'money',
}
comparison-table
Side-by-side comparison of multiple items across a set of criteria. Used in competitor analysis, feature comparisons, etc.
{
type: 'comparison-table',
label: 'Competitor Comparison',
rows: ['Pricing', 'Free Tier', 'API Access', 'Support'],
columns: ({ collections }) =>
(collections.competitors ?? []).map(c => ({
key: c.id,
label: c.name,
values: {
'Pricing': c.pricing,
'Free Tier': c.hasFree ? 'Yes' : 'No',
'API Access': c.hasApi ? 'Yes' : 'No',
'Support': c.support,
},
})),
}
feature-matrix
Checkbox-style matrix of features vs. options. Common for pricing plan comparison.
{
type: 'feature-matrix',
label: 'Plan Feature Matrix',
features: ['Unlimited users', 'API access', 'SSO', 'Audit log', 'SLA'],
plans: ({ collections }) =>
(collections.plans ?? []).map(p => ({
label: p.name,
features: p.features ?? [], // string[] of included feature names
})),
}
timeline
A vertical list of dated milestones.
{
type: 'timeline',
label: 'Launch Milestones',
items: ({ collections }) =>
(collections.milestones ?? []).map(m => ({
date: m.date,
label: m.title,
done: m.done,
color: m.done ? 'success' : 'default',
})),
}
Custom Dashboard Layouts
For complex tools with multiple tabs, columns, and mixed content types, use the layout property to take full control of the rendering.
Layout structure
layout: {
rows: [ // top-level rows, rendered vertically
{
cols: [ // columns within a row
{ type: 'form', span: 6 }, // left: form fields
{ type: 'stats', span: 6 }, // right: result stats
]
},
{
cols: [
{ type: 'viz', vizIndex: 0, span: 12 }, // full-width viz
]
},
{
cols: [
{
type: 'collection',
collectionKey: 'interviews',
span: 12,
}
]
},
]
}
Tab layout
Use type: 'tabs' as a LayoutItem to render a UTabs component. Each tab can contain nested rows/cols:
layout: {
tabs: [
{
id: 'questions',
label: 'Questions',
badge: ({ collections }) => collections.questions?.length,
rows: [
{ cols: [{ type: 'collection', collectionKey: 'questions', span: 12 }] }
],
},
{
id: 'interviews',
label: 'Interviews',
badge: ({ collections }) => collections.interviews?.length,
rows: [
{ cols: [{ type: 'collection', collectionKey: 'interviews', span: 12 }] }
],
},
{
id: 'patterns',
label: 'Patterns',
rows: [
{ cols: [
{ type: 'stats', span: 12 },
{ type: 'viz', span: 12 },
]},
],
},
]
}
LayoutItem types
type | Renders | Additional props |
|---|---|---|
'form' | ProtoForm with schema fields | sectionIndex? |
'stats' | ProtoStatGrid from results.stats | — |
'viz' | ProtoViz | vizIndex (0-based) |
'card' | ProtoCard | cardIndex |
'collection' | ProtoCrudList or ProtoCrudTable | collectionKey |
'section' | SectionDef group | sectionKey |
'tabs' | UTabs with nested layouts | tabs array |
span (column width)
Uses a 12-column grid. span: 6 = half width, span: 12 = full width. Default is 12.
Actions toolbar
actions: [
{
type: 'copy-text',
label: 'Copy Interview Script',
icon: 'i-lucide-clipboard',
text: ({ fields, collections }) =>
(collections.questions ?? [])
.map((q, i) => `${i + 1}. ${q.text}`)
.join('\n'),
},
{
type: 'export-markdown',
label: 'Export Summary',
icon: 'i-lucide-download',
markdown: ({ fields, derived, collections }) =>
`# Interview Summary\n\n**${derived.interviewCount} interviews**\n\n## Pain Points\n${derived.painPoints?.join('\n- ')}`,
},
{
type: 'reset',
label: 'Reset All Data',
icon: 'i-lucide-trash-2',
confirm: true, // shows a confirmation dialog before resetting
},
]
ProtoActionBar renders these as a toolbar with icon buttons when schema.actions is defined.
Connections
The produces/consumes system lets tools share computed data without tight coupling, forming a reactive data graph across your entire application.
Overview
All composables auto-imported by protokit — from the high-level usePrototype facade to the low-level Y.js primitives.