Visualizations & Layouts

Built-in visualization types (progress, bar chart, 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.

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

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.