Skip to content

Funnel and Pyramid

Stage-based series without axes: flat data with stageField/valueField.

Funnel

Stages run top to bottom, width is proportional to the value. funnel — rectangular stages, cone-funnel — trapezoids tapering to the next stage.

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Activation funnel' },
    series: [{ type: 'cone-funnel', stageField: 'stage', valueField: 'value', name: 'Users' }],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { stage: 'Visits', value: 12400 },
    { stage: 'Sign-ups', value: 5300 },
    { stage: 'Activations', value: 2900 },
    { stage: 'Subscriptions', value: 1150 },
    { stage: 'Renewals', value: 780 },
  ];
}

Spacing and outside labels

itemSpacing — the gap between segments; label.placement: 'outside' moves labels out to the right. The shape geometry does not depend on labels — the width is set by widthRatio:

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Funnel: spacing and outside labels' },
    series: [
      {
        type: 'funnel',
        stageField: 'stage',
        valueField: 'count',
        itemSpacing: 10,
        label: {
          placement: 'outside',
          formatter: ({ stage, value }) => `${stage} — ${value.toLocaleString('en-US')}`,
        },
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { stage: 'Visits', count: 12500 },
    { stage: 'Sign-ups', count: 6400 },
    { stage: 'Activations', count: 3100 },
    { stage: 'Subscriptions', count: 1400 },
    { stage: 'Renewals', count: 900 },
  ];
}

Cone funnel with outside labels

Trapezoidal stages; the callout line starts at the slanted edge. Inside labels get an outline in the background color (readable on any segment):

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Cone funnel: outside labels' },
    series: [
      {
        type: 'cone-funnel',
        stageField: 'stage',
        valueField: 'count',
        itemSpacing: 2,
        label: {
          placement: 'outside',
          formatter: ({ stage, value }) => `${stage} — ${value.toLocaleString('en-US')}`,
        },
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { stage: 'Leads', count: 8200 },
    { stage: 'Qualified', count: 4900 },
    { stage: 'Demo', count: 2300 },
    { stage: 'Contract', count: 1100 },
    { stage: 'Payment', count: 750 },
  ];
}

Pyramid

Layer height is proportional to the value; reverse flips the apex downward.

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Company structure' },
    series: [{ type: 'pyramid', stageField: 'level', valueField: 'count', name: 'People' }],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { level: 'C-level', count: 6 },
    { level: 'Managers', count: 28 },
    { level: 'Team leads', count: 90 },
    { level: 'Engineers', count: 420 },
    { level: 'Interns', count: 160 },
  ];
}

Spacing and inside labels

itemSpacing slices the pyramid into layers; label.placement: 'inside' — labels in the segments with an auto-contrast color:

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Pyramid: spacing and inside labels' },
    series: [
      {
        type: 'pyramid',
        stageField: 'level',
        valueField: 'people',
        itemSpacing: 6,
        label: { placement: 'inside', fontWeight: 'bold' },
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { level: 'CEO', people: 2 },
    { level: 'Directors', people: 9 },
    { level: 'Managers', people: 34 },
    { level: 'Engineers', people: 120 },
    { level: 'Interns', people: 45 },
  ];
}

Options

Options common to all series (name, showInLegend, tooltip.renderer, …) are covered in Common series options.

OptionSeriesDefaultDescription
stageFieldallstage name and value
valueFieldallstage name and value
fillsallpalettestage colors
itemSpacingallfunnel 4, pyramid 0gap between segments
widthRatioall0.62fraction of the area width given to the shape (independent of labels)
reversepyramidfalseapex at the bottom
label.enabledbooleantruestage labels
label.placement'inside' | 'outside'funnel 'inside'; pyramid 'outside'position (shared by all segments)
label.formatter({ datum, stage, value }) => stringstage · valuecontent
label.fontSizePixels12font
label.fontWeightstring | numbernormalfont weight
label.colorColorValueinside — auto-contrast; outside — foregroundcolor
calloutLine.enabledbooleantrue when outsideline to the outside label
calloutLine.lengthPixels14line length
calloutLine.strokeColorValuesegment colorline color
calloutLine.strokeWidthPixels1line width