Areas in Vue

Using ApexCharts with Vue

This Areas 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 area</h1>
      <p>
        Pick a traffic source. Each section's visits stay on the chart, faded
        with an outline, and the visits that source brought are drawn solid in
        front from the same baseline. In the stacked chart below, the source's
        visits form their own stack.
      </p>
      <div class="actions">
        <span>Source</span>
        <button data-source="search" class="active">Search</button>
        <button data-source="social">Social</button>
        <button data-source="">All (clear)</button>
      </div>
      <div class="chart-wrap">
        <div id="chart">
          <apexchart
            type="area"
            height="320"
            :options="chartOptions"
            :series="series"
          ></apexchart>
        </div>
      </div>
      <div class="chart-wrap">
        <div id="chart2">
          <apexchart
            type="area"
            height="320"
            :options="chartOptions1"
            :series="series1"
          ></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.
//
// Visits per day to each section of a website (the wholes), and the visits
// one traffic source brought (the parts). Picking a source keeps every
// section on both charts: each area is drawn faded at its full size with its
// outline, and the source's visits are solid in front, from the same
// baseline. In the stacked chart the parts form their own stack.
var DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
var SECTIONS = ['Docs', 'Blog']
var VISITS = [
  [62, 70, 74, 68, 60, 34, 30],
  [28, 34, 40, 37, 42, 48, 44]
]
var SOURCES = {
  search: [
    [40, 44, 47, 42, 37, 20, 18],
    [12, 15, 18, 16, 18, 20, 19]
  ],
  social: [
    [6, 8, 9, 8, 7, 6, 5],
    [10, 12, 14, 14, 16, 21, 19]
  ]
}

function seriesFor(source) {
  return SECTIONS.map(function (name, i) {
    var s = { name: name, data: VISITS[i] }
    if (source) s.highlightData = SOURCES[source][i]
    return s
  })
}

// One call per chart and pick: the parts go in, each chart animates from what
// is on screen. Clearing is the same call without them.
function pickSource(source) {
  document.querySelectorAll('[data-source]').forEach(function (b) {
    b.classList.toggle('active', b.getAttribute('data-source') === source)
  })
  return Promise.all(
    ['visits', 'stacked-visits'].map(function (id) {
      return ApexCharts.getChartByID(id).updateSeries(seriesFor(source))
    })
  )
}

function wireSources() {
  document.querySelectorAll('[data-source]').forEach(function (b) {
    b.addEventListener('click', function () {
      pickSource(b.getAttribute('data-source'))
    })
  })
}

export default {
components: {
apexchart: VueApexCharts,
},
data: function () {
return {
series: seriesFor('search'),
chartOptions: {
chart: {
  id: 'visits',
  type: 'area',
  height: 320,
  toolbar: { show: false },
},
colors: ['#0284c7', '#16a34a'],
stroke: { curve: 'smooth', width: 2 },
dataLabels: { enabled: false },
xaxis: { categories: DAYS },
yaxis: { title: { text: 'Visits (thousands)' } },
legend: { position: 'bottom' },
tooltip: { shared: true, intersect: false },
},
series1: seriesFor('search'),
chartOptions1: {
chart: {
  id: 'stacked-visits',
  type: 'area',
  height: 320,
  stacked: true,
  toolbar: { show: false },
},
colors: ['#0284c7', '#16a34a'],
stroke: { curve: 'straight', width: 2 },
fill: { type: 'solid', opacity: 0.85 },
dataLabels: { enabled: false },
xaxis: { categories: DAYS },
yaxis: { title: { text: 'Visits (thousands)' } },
legend: { position: 'bottom' },
tooltip: { shared: true, intersect: false },
},
sourceTimer: null,
}
},
mounted: function () {
  // The vue-apexcharts wrapper owns the render, so reach the live instances by
  // their chart.id. (seriesFor / pickSource live in the shared head script.)
  var me = this
  me.sourceTimer = window.setInterval(function () {
    if (!ApexCharts.getChartByID('visits') || !ApexCharts.getChartByID('stacked-visits')) return
    window.clearInterval(me.sourceTimer)
    wireSources()
  }, 50)
},
beforeDestroy: function () {
  window.clearInterval(this.sourceTimer)
},,
}
</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);
  margin: 0 0 16px;
}
.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>