Visualizations & Layouts

Built-in visualization types (progress, bar chart, line chart, area chart, pie/donut chart, funnel, stat cards, comparison table, feature matrix, timeline) and custom dashboard layouts with tabs, rows, and columns.

Visualizations & Layouts

Built-in visualization types

Add a visualizations array to your schema to render charts and tables below the results section. All chart types are available as standalone Viz* components and as values in VizDef.type.

typeComponentBest for
progressVizProgressBarSingle metric vs. goal
benchmarkVizBenchmarkBarPositioning relative to a range
bar-chartVizBarChartRanked or categorical values
line-chartVizLineChartSingle time-series trend
area-chartVizAreaChartMulti-series trends with fill
pie-chartVizPieChartPart-of-whole distribution
donut-chartVizPieChart (donut)Part-of-whole with centre total
funnel-chartVizFunnelChartStep-by-step conversion
stat-cardsVizStatCardsKPI overview with trends
comparison-tableVizComparisonTableSide-by-side attribute comparison
feature-matrixVizFeatureMatrixFeature coverage matrix
timelineVizTimelineDated milestone list

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
    })),
}

line-chart

A smooth SVG line chart with an optional gradient fill and axes. Accepts a flat array of { label, value } points.

{
  type: 'line-chart',
  title: 'Monthly Revenue',
  config: {
    data: ({ derived }) => derived.revenueByMonth,  // { label, value }[]
    showAxes: true,
    unit: '$',
    color: 'var(--color-emerald-500)',
  },
}

area-chart

A multi-series SVG area chart. Each series renders its own filled region and line, making it ideal for comparing trends over the same time axis.

{
  type: 'area-chart',
  title: 'Revenue vs Target',
  config: {
    series: ({ derived }) => [
      { label: 'Actual',  data: derived.revenueByMonth, color: 'var(--color-primary-500)' },
      { label: 'Target',  data: derived.targetByMonth,  color: 'var(--color-emerald-500)' },
    ],
    showAxes: true,
    unit: '$',
  },
}

pie-chart / donut-chart

An SVG pie chart with a legend. Use donut-chart to render a donut variant that displays the total value in the centre.

{
  type: 'donut-chart',
  title: 'Traffic Sources',
  config: {
    data: ({ derived }) => derived.channelBreakdown,  // { label, value, color? }[]
    unit: '%',
    showLegend: true,
  },
}

funnel-chart

A horizontal funnel that shows step-by-step conversion rates. Each bar is sized relative to the first step, and the percentage conversion from the previous step is shown between bars.

{
  type: 'funnel-chart',
  title: 'Acquisition Funnel',
  config: {
    data: ({ derived }) => [
      { label: 'Visitors',   value: derived.visitors },
      { label: 'Sign-ups',   value: derived.signups },
      { label: 'Trial',      value: derived.trials },
      { label: 'Paid',       value: derived.paid },
    ],
    showConversion: true,
  },
}

stat-cards

A responsive grid of KPI cards. Each card shows a label, value, optional trend arrow with percentage, and an optional sparkline.

{
  type: 'stat-cards',
  config: {
    cols: 3,
    cards: ({ derived }) => [
      {
        label: 'Monthly Revenue',
        value: `$${derived.mrr.toLocaleString()}`,
        trend: derived.mrrGrowthPct,
        trendLabel: 'vs last month',
        icon: 'i-lucide-dollar-sign',
        color: 'emerald',
        sparkline: derived.mrrHistory,   // number[]
      },
      {
        label: 'Active Users',
        value: derived.activeUsers,
        trend: derived.userGrowthPct,
        icon: 'i-lucide-users',
        color: 'primary',
      },
    ],
  },
}

The color field accepts 'primary' | 'emerald' | 'rose' | 'amber' | 'violet' | 'sky' | 'neutral' and controls the icon background, icon tint, and sparkline colour.

timeline

A vertical list of dated milestones.

{
  type: 'timeline',
  title: 'Launch Milestones',
  config: {
    items: ({ collections }) =>
      (collections.milestones ?? []).map(m => ({
        title: m.title,
        date:   m.date,
        status: m.done ? 'done' : 'upcoming',
      })),
  },
}

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

typeRendersAdditional props
'form'ProtoForm with schema fieldssectionIndex?
'stats'ProtoStatGrid from results.stats—
'viz'ProtoVizvizIndex (0-based)
'card'ProtoCardcardIndex
'collection'ProtoCrudList or ProtoCrudTablecollectionKey
'section'SectionDef groupsectionKey
'tabs'UTabs with nested layoutstabs 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.

Need a Landing Page?

Modern landing pages with optional modules (blog, docs, forms, i18n). Let's discuss your project.

Build Your MVP

Full-stack SaaS development. Expert in database design, multi-tenancy, and scalable architecture.

Deployment Help

Dockerize your backend, set up CI/CD pipelines, deploy to Cloudflare or Hetzner. Early-stage setup.

Suggest a SaaS Tool

Missing a calculator or tool? Suggest what you'd like to see on our site.