Funnel in React

Using ApexCharts with React

This Funnel example wires ApexCharts into React through the react-apexcharts component.

Install it with npm install react-apexcharts apexcharts, then mount the chart with <Chart type="..." options={options} series={series} />.

React charts guide
import React from 'react'
import ReactApexChart from 'react-apexcharts'
import ApexCharts from 'apexcharts'
import './styles.css'

import 'apexcharts/features/highlight-filter'

// Shared by the vanilla-js, React and Vue builds.
//
// Visitors at each step of a signup funnel (the wholes) and how many of them
// came from one device (the parts). Each stage keeps its full width, faded,
// with the device's share solid and centred inside it.
var STEPS = [
  'Visited',
  'Viewed pricing',
  'Started trial',
  'Activated',
  'Subscribed',
]
var VISITORS = [12400, 7300, 3900, 2100, 1200]
var DEVICES = {
  mobile: [7900, 3900, 1600, 700, 330],
  desktop: [4500, 3400, 2300, 1400, 870],
}

function seriesFor(device) {
  var s = { name: 'Visitors', data: VISITORS }
  if (device) s.highlightData = DEVICES[device]
  return [s]
}

function pickDevice(chart, device) {
  document.querySelectorAll('[data-device]').forEach(function (b) {
    b.classList.toggle('active', b.getAttribute('data-device') === device)
  })
  return chart.updateSeries(seriesFor(device))
}

function wireDevices(chart) {
  document.querySelectorAll('[data-device]').forEach(function (b) {
    b.addEventListener('click', function () {
      pickDevice(chart, b.getAttribute('data-device'))
    })
  })
}

const ApexChart = () => {
  const [state, setState] = React.useState({
    series: seriesFor('mobile'),
    options: {
      chart: {
        id: 'signup',
        type: 'funnel',
        height: 360,
        toolbar: { show: false },
      },
      colors: ['#0284c7'],
      plotOptions: {
        bar: { barHeight: '82%', borderRadius: 0 },
      },
      dataLabels: {
        enabled: true,
        formatter: function (v, opt) {
          return (
            opt.w.globals.labels[opt.dataPointIndex] + ': ' + v.toLocaleString()
          )
        },
      },
      xaxis: { categories: STEPS },
      legend: { show: false },
    },
  })

  React.useEffect(() => {
    // The react-apexcharts wrapper owns the render, so reach the live instance
    // by its chart.id. (seriesFor / pickDevice live in the shared head script.)
    const timer = window.setInterval(() => {
      const chart = ApexCharts.getChartByID('signup')
      if (!chart) return
      window.clearInterval(timer)
      wireDevices(chart)
    }, 50)
    return () => window.clearInterval(timer)
  }, [])

  return (
    <div>
      <div className="wrap">
        <h1>Which device carries the funnel?</h1>
        <p>
          Pick a device. Every stage keeps its full width, faded, and the
          visitors on that device are drawn solid and centred inside it, so you
          can read where one device drops off faster than the other.
        </p>
        <div className="actions">
          <span>Device</span>
          <button data-device="mobile" className="active">
            Mobile
          </button>
          <button data-device="desktop">Desktop</button>
          <button data-device="">All (clear)</button>
        </div>
        <div className="chart-wrap">
          <div id="chart">
            <ReactApexChart
              options={state.options}
              series={state.series}
              type="funnel"
              height={360}
            />
          </div>
        </div>
      </div>
    </div>
  )
}

export default ApexChart