Lines in Vue

Using ApexCharts with Vue

This Lines 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>Highlight a part of every line</h1>
      <p>
        Pick a kind of customer. Each store's orders stay on the chart as a
        dashed line at their full value, and the orders that customer placed are
        drawn solid in the same colour. Labels and tooltips state the part; the
        markers stay on the full line.
      </p>
      <div class="actions">
        <span>Customer</span>
        <button data-customer="returning" class="active">Returning</button>
        <button data-customer="first">First order</button>
        <button data-customer="">All (clear)</button>
      </div>
      <div class="chart-wrap">
        <div id="chart">
          <apexchart
            type="line"
            height="380"
            :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.
//
// Orders per month by store (the wholes), and the orders that came from one
// kind of customer (the parts). Picking a customer keeps both stores on the
// chart: each line is drawn dashed at its full value, with that customer's
// orders solid in the same colour, on the same axis.
var MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug']
var STORES = ['Online store', 'Retail']
var ORDERS = [
  [420, 460, 510, 480, 560, 610, 590, 650],
  [300, 320, 310, 340, 360, 330, 380, 400]
]
var CUSTOMERS = {
  returning: [
    [250, 270, 300, 290, 340, 370, 360, 400],
    [210, 220, 215, 240, 250, 230, 260, 280]
  ],
  first: [
    [170, 190, 210, 190, 220, 240, 230, 250],
    [90, 100, 95, 100, 110, 100, 120, 120]
  ]
}

function seriesFor(customer) {
  return STORES.map(function (name, i) {
    var s = { name: name, data: ORDERS[i] }
    if (customer) s.highlightData = CUSTOMERS[customer][i]
    return s
  })
}

// One call per pick: the parts go in, the chart animates from what is on
// screen. Clearing is the same call without them.
function pickCustomer(chart, customer) {
  document.querySelectorAll('[data-customer]').forEach(function (b) {
    b.classList.toggle('active', b.getAttribute('data-customer') === customer)
  })
  return chart.updateSeries(seriesFor(customer))
}

function wireCustomers(chart) {
  document.querySelectorAll('[data-customer]').forEach(function (b) {
    b.addEventListener('click', function () {
      pickCustomer(chart, b.getAttribute('data-customer'))
    })
  })
}

export default {
components: {
apexchart: VueApexCharts,
},
data: function () {
return {
series: seriesFor('returning'),
chartOptions: {
chart: {
  id: 'orders',
  type: 'line',
  height: 380,
  toolbar: { show: false },
},
colors: ['#0f766e', '#ea580c'],
stroke: { curve: 'smooth', width: 3 },
markers: { size: 4, strokeWidth: 0, hover: { size: 6 } },
dataLabels: {
  enabled: true,
  style: { fontSize: '11px' },
  background: { enabled: true, borderWidth: 0, opacity: 0.85 },
},
xaxis: { categories: MONTHS },
yaxis: { min: 0, title: { text: 'Orders' } },
highlightFilter: { line: { dashArray: 5 } },
legend: { position: 'bottom' },
tooltip: { shared: true, intersect: false },
},
customerTimer: null,
}
},
mounted: function () {
  // The vue-apexcharts wrapper owns the render, so reach the live instance by
  // its chart.id. (seriesFor / pickCustomer live in the shared head script.)
  var me = this
  me.customerTimer = window.setInterval(function () {
    var chart = ApexCharts.getChartByID('orders')
    if (!chart) return
    window.clearInterval(me.customerTimer)
    wireCustomers(chart)
  }, 50)
},
beforeDestroy: function () {
  window.clearInterval(this.customerTimer)
},,
}
</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: #0f766e;
  border-color: #0f766e;
}
</style>