Funnel in Vue

Using ApexCharts with Vue

This Funnel example wires ApexCharts into Vue through the vue3-apexcharts component.

Install it with npm install vue3-apexcharts apexcharts, then mount the chart with <apexchart type="..." :options="options" :series="series" />.

Vue charts guide
<template>
  <div>
    <div class="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 class="actions">
        <span>Device</span>
        <button data-device="mobile" class="active">Mobile</button>
        <button data-device="desktop">Desktop</button>
        <button data-device="">All (clear)</button>
      </div>
      <div class="chart-wrap">
        <div id="chart">
          <apexchart
            type="funnel"
            height="360"
            :options="chartOptions"
            :series="series"
          ></apexchart>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import VueApexCharts from 'vue-apexcharts'
import ApexCharts from 'apexcharts'

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

export default {
components: {
apexchart: VueApexCharts,
},
data: function () {
return {
series: seriesFor('mobile'),
chartOptions: {
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 },
},
deviceTimer: null,
}
},
mounted: function () {
  // The vue-apexcharts wrapper owns the render, so reach the live instance by
  // its chart.id. (seriesFor / pickDevice live in the shared head script.)
  var me = this
  me.deviceTimer = window.setInterval(function () {
    var chart = ApexCharts.getChartByID('signup')
    if (!chart) return
    window.clearInterval(me.deviceTimer)
    wireDevices(chart)
  }, 50)
},
beforeDestroy: function () {
  window.clearInterval(this.deviceTimer)
},,
}
</script>

<style>
body {
  font-family:
    -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: #fafbfc;
  color: #2c3e50;
  margin: 0;
  padding: 32px 16px;
}
.wrap {
  max-width: 720px;
  margin: 0 auto;
}
h1 {
  font-size: 22px;
  margin: 0 0 8px;
}
p {
  color: #5b6b78;
  line-height: 1.5;
  margin: 0 0 20px;
}
.chart-wrap {
  background: #fff;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
}
.actions {
  margin: 0 0 14px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.actions span {
  font-size: 13px;
  color: #5b6b78;
  margin-right: 4px;
}
.actions button {
  color: #2c3e50;
  background: #fff;
  border: 1px solid #cfd8e3;
  padding: 6px 14px;
  font-weight: 600;
  font-size: 13px;
  border-radius: 6px;
  cursor: pointer;
}
.actions button.active {
  color: #fff;
  background: #0284c7;
  border-color: #0284c7;
}
</style>