DocumentationNuxt Charts v3

Funnel Chart

Learn how to implement and customize Funnel Charts in your Nuxt application.

The FunnelChart component visualizes how a quantity narrows across sequential stages — perfect for conversion, sales-pipeline and drop-off analysis. New in v3.

Basic Usage

Conversion Funnel

End-to-end9.6%
Last 30 days

Variants

Set variant="layered" for curved, nested layers with on-shape value / percentage / stage labels — a richer look for hero sections and dashboards. The default variant="default" renders the classic trapezoid funnel, and last-shape-type="rectangle" squares off the final stage.

Marketing Pipeline

Closed won1.5K
Q3 2025 · all channels
Impressions
128.4K
entry
Clicks
42.8K
-67%
Leads
14.6K
-66%
Demos
5.2K
-64%
Closed won
1.5K
-72%

Spacing and Value Formatting

stage-gap sets the vertical gap between stages in pixels (default 4). It applies to the classic variant="default" funnel; the layered variant spaces its own nested layers.

value-formatter controls the value rendered inside each shape and in the tooltip, in both variants — use it to abbreviate large numbers or add units.

<FunnelChart
  :data="data"
  :categories="categories"
  :height="260"
  :stage-gap="6"
  last-shape-type="rectangle"
  :value-formatter="(value) => value.toLocaleString()"
/>

Label Styling

Funnel labels split across two surfaces, and which one you reach for is decided by the label, not by taste:

WhatWhereWhy
Sizelabel-size propThe layered percentage badge is a real SVG pill sized around its own text. CSS cannot feed a computed font size back into that geometry, so a token here would desync the pill from its label.
ColorCSS token / theme.funnelPure paint. Belongs in the cascade so it inherits dark mode and your brand colors.
WeightCSS token / theme.funnelSame.

Size

label-size defaults to 'auto', which fits the labels to the plot height and the per-stage width — one chart stays legible in a 220px card and in a full-width tile without you touching it. Pin it with a keyword, a pixel number, or per label role:

<!-- keyword: sm = 10px, md = 12px, lg = 14px -->
<FunnelChart
  :data="data"
  :categories="categories"
  variant="layered"
  :height="220"
  label-size="sm"
/>

<!-- one pixel base; each role derives from it -->
<FunnelChart
  :data="data"
  :categories="categories"
  variant="layered"
  :height="220"
  :label-size="11"
/>

<!-- per role -->
<FunnelChart
  :data="data"
  :categories="categories"
  variant="layered"
  :height="220"
  :label-size="{ value: 13, percentage: 10, name: 11 }"
/>

A badge that still cannot fit its stage is dropped rather than allowed to overlap its neighbours.

Color and weight

Every label reads a --vc-funnel-* token. The defaults chain into the chart's own axis and tick tokens, which resolve against Nuxt UI's --ui-* layer first — so light/dark and a brand color change are inherited with no configuration at all.

TokenApplies toDefault
--vc-funnel-value-colorStage value--vc-axis-label-color
--vc-funnel-value-weightStage value600
--vc-funnel-value-sizeStage value, default variant--vc-legend-size
--vc-funnel-label-colorStage name--vc-tick-color
--vc-funnel-label-weightStage name500
--vc-funnel-badge-colorPercentage text--vc-tick-color
--vc-funnel-badge-weightPercentage text600
--vc-funnel-badge-bgPercentage pill fill--vc-surface-bg
--vc-funnel-badge-opacityPercentage pill fill opacity0.82
--vc-funnel-badge-borderPercentage pill border--vc-axis-line-color

Set them globally in your CSS:

.vue-chrts {
  --vc-funnel-value-color: var(--ui-primary);
  --vc-funnel-value-weight: 700;
}

Or per chart, via the theme prop:

<FunnelChart
  :data="data"
  :categories="categories"
  variant="layered"
  :height="220"
  :theme="{
    funnel: {
      valueColor: 'var(--ui-primary)',
      valueWeight: 700,
      labelColor: 'var(--ui-text-dimmed)',
      badgeBg: 'var(--ui-bg-elevated)',
      badgeOpacity: 1,
    },
  }"
/>

Props

PropTypeDefaultDescription
dataT[] | number[]RequiredStage values, in the same order as categories, or records with nameKey / valueKey.
nameKeykeyof TundefinedRecord key holding the stage name, when data is an array of objects.
valueKeykeyof TundefinedRecord key holding the stage value, when data is an array of objects.
categoriesRecord<string, BulletLegendItemInterface>RequiredMaps each stage to its name and color.
heightnumberRequiredHeight of the chart in pixels.
variant'default' | 'layered''default'Visual variant of the funnel.
lastShapeType'triangle' | 'rectangle''triangle'Shape of the final (smallest) stage.
showValueLabelbooleantrueShow the value label on each stage.
showStageLabelbooleantrueShow the stage name inside each shape when it fits.
stageGapnumber4Vertical gap between classic funnel stages, in pixels.
valueFormatter(value: number, index: number) => stringundefinedFormat values rendered inside shapes and in the tooltip.
showPercentagebooleantruelayered only. Show the percentage badge.
labelSize'auto' | 'sm' | 'md' | 'lg' | number | FunnelLabelSizes'auto'Size of the funnel's labels. See Label Styling.
hideLegendbooleanfalseIf true, hides the chart legend.
legendPositionLegendPositionundefinedPosition of the legend.
legendStyleRecord<string, string>undefinedOptional inline CSS styles for the legend container.
hideTooltipbooleanfalseIf true, hides the tooltip.
tooltipTitleFormatter(data: T) => string | numberundefinedCustom formatter for tooltip titles.
tooltipTooltipConfigundefinedTooltip configuration.
themeChartThemeundefinedPer-chart appearance overrides, incl. theme.funnel label color/weight.
durationnumberundefinedAnimation duration in milliseconds.

Data Format

data is a positional array of numbers, ordered the same as categories. List the largest value first for a top-down funnel.

types.ts
const data: number[] = [5000, 2300, 1200, 480];