developer-tools #apexcharts #unit-chart #waffle-chart #dashboard #vue #licensing #frontend-maintenance

ApexCharts 6.6 unit and waffle charts are a product decision, not a type swap

ApexCharts 6.6 adds premium unit and waffle charts with trial watermarks, tree-shakeable imports, and TypeScript options. Treat adoption as a license, fixture, accessibility, and rollback contract.

A new chart type looks like a one-line config change. In a SaaS dashboard, it is rarely that small.

ApexCharts 6.6.0, published on 2026-07-27, introduces the first premium chart type in the library: unit, with waffle as a thin alias. The release is explicit that the feature renders in trial mode with an APEXCHARTS watermark until a license key is set, that other chart types stay free and unwatermarked, and that existing configs render unchanged. Those three sentences already change the maintenance conversation. You are no longer choosing between pie and donut. You are choosing whether a paid, watermarked, opt-in visualization path belongs in a product surface that support, sales, and compliance will see.

The question is not whether the demo looks clever. The question is whether the change survives license review, handoff, SSR, accessibility checks, and a clean rollback when the new type is the wrong fit.

A new chart type is a product contract, not a one-line type swap

What 6.6 actually shipped

Ground the work in the primary release, not a secondary summary. The ApexCharts v6.6.0 release notes describe:

Claim from the releaseOperational meaning
New premium chart type unitDiscrete mark per unit of value: dots, pictograms, waffles, beeswarms
waffle aliasThin preset of unit that selects the grid layout with square cells
Trial mode watermark until a key is setCustomer-visible branding unless licensing is configured
Tree-shakeable import 'apexcharts/unit'Bundle and entry-point decisions, not only options JSON
Six layouts via plotOptions.unit.layoutGeometry and interaction surface is larger than one sample
TypeScript coverage for plotOptions.unit and chart.type 'unit'|'waffle'Types help, they do not replace visual fixtures
No breaking API changes for existing chart typesCore charts can stay put while you evaluate the premium path separately
License manager gains signature verificationLicense injection is part of the runtime surface
Zoom-out edge-case fixOrthogonal fix; do not fold it into the unit adoption ticket

npm currently lists apexcharts latest as 6.6.1. There is no matching GitHub release tag or body for v6.6.1 at collection time, so this article does not invent patch details for 6.6.1. Pin and test against the version you actually install, and treat the verified GitHub body of 6.6.0 as the feature contract for unit and waffle.

The official waffle guide states the same product boundary: waffle shares the unit engine, is a Premium chart type, renders with an APEXCHARTS watermark without a license key, and is enabled with import 'apexcharts/unit'. [Source: https://apexcharts.com/docs/chart-types/waffle-chart/]

Why a premium chart type is a product decision

Most dashboard teams treat chart libraries as presentation glue. Bar, line, area, and pie already sit behind KPI cards. A designer asks for a “friendlier pie,” a developer finds type: 'waffle', and the PR title becomes “improve energy mix visualization.”

That framing hides the real decisions:

  1. Commercial path — premium feature, trial watermark, license key placement, and who pays.
  2. Default chart inventory — which screens are allowed to depend on premium types.
  3. Accessibility and density — discrete marks behave differently from continuous geometry for keyboard users and dense data.
  4. Wrapper and SSR lag — core package movement does not move vue3-apexcharts automatically.
  5. Rollback — can you return to a free chart type without a product incident?

The release itself frames unit as the first premium chart type and states that every other chart type stays free and is never watermarked. That asymmetry is the point. You can keep shipping ordinary charts under the existing license conversation and still refuse a watermarked or paid path on customer-facing admin screens until legal and product agree.

Five gates before adopting unit or waffle charts

Gate 1 — License and watermark before aesthetics

Official npm documentation for ApexCharts describes premium features and licensing: without a valid key, premium features still work in trial mode but show an APEXCHARTS watermark; a valid key removes it. The unit chart type, aliased by waffle, is the premium chart type called out there. License can be set globally with ApexCharts.setLicense(...), via window.Apex = { license: '...' }, or per chart with chart.license. [Source: https://www.npmjs.com/package/apexcharts]

That means a staging environment that “looks fine” without a key is not proof of production readiness. It is proof that trial mode is easy to forget.

Use an explicit startup path and fail closed in environments that must not show vendor chrome:

 1// resources/js/charts/apex-license.ts
 2import ApexCharts from "apexcharts";
 3
 4/**
 5 * Call once on the client before any chart render.
 6 * Keep the raw key out of the SPA bundle when your threat model requires it:
 7 * inject from a server-rendered meta tag or a short-lived config endpoint.
 8 */
 9export function configureApexLicense(): void {
10  const key = import.meta.env.VITE_APEXCHARTS_LICENSE_KEY as string | undefined;
11
12  if (!key) {
13    if (import.meta.env.PROD) {
14      throw new Error(
15        "Missing VITE_APEXCHARTS_LICENSE_KEY. Refusing premium chart boot in production."
16      );
17    }
18    // Local dev may intentionally exercise trial watermark screenshots.
19    return;
20  }
21
22  ApexCharts.setLicense(key);
23}

Pair that with a product rule, not only an env var:

EnvironmentAllowed unit/waffle modeEvidence
Local developer machineTrial watermark OK for explorationScreenshot labeled trial
Shared staging / client demoLicensed onlyScreenshot without watermark + key injection path documented
ProductionLicensed only, or free chart type fallbackSame as staging, plus rollback type named

Pricing and plan packaging change over time. The public pricing and license pages describe Community, Commercial, Pro, Premium, and OEM options; waffle documentation currently points premium waffle availability at Premium and OEM plans. Do not hard-code prices into architecture docs as eternal facts. Link the current pricing and license pages in the PR and re-check them when finance renews.

Gate 2 — Import path and TypeScript surface

The release and waffle docs agree on the opt-in import. With a tree-shakeable core, waffle and unit are not free just because the package is installed:

 1// resources/js/charts/unit-chart.ts
 2import ApexCharts from "apexcharts/core";
 3import "apexcharts/unit"; // serves both chart.type 'unit' and 'waffle'
 4
 5import { configureApexLicense } from "./apex-license";
 6
 7configureApexLicense();
 8
 9export type MixSlice = {
10  label: string;
11  value: number;
12};
13
14export function renderEnergyWaffle(
15  el: HTMLElement,
16  slices: MixSlice[]
17): ApexCharts {
18  const chart = new ApexCharts(el, {
19    chart: {
20      type: "waffle",
21      height: 320,
22      animations: { enabled: true },
23    },
24    series: slices.map((s) => s.value),
25    labels: slices.map((s) => s.label),
26    plotOptions: {
27      unit: {
28        // waffle alias presets grid + square; options still live under plotOptions.unit
29        grid: {
30          total: 100,
31          columns: 10,
32          fillFrom: "bottom",
33        },
34      },
35    },
36    legend: {
37      show: true,
38      position: "bottom",
39    },
40  });
41
42  chart.render();
43  return chart;
44}

The same release documents a direct unit example with layouts such as grouped:

 1// Minimal unit example adapted from the v6.6.0 release body.
 2// Prefer fixture data that matches a real KPI, not marketing sample counts.
 3new ApexCharts(document.querySelector("#vote-units"), {
 4  chart: { type: "unit" },
 5  series: [276, 266, 3],
 6  labels: ["For", "Against", "Abstain"],
 7  plotOptions: {
 8    unit: {
 9      layout: "grouped",
10    },
11  },
12});

Layouts listed in the release body: grouped (default), packed, columns, grid, grid with split: true, and scatter (including beeswarm and bubble variants). Shapes include circle, square, and image pictograms. Transitions include group, flow, and identity. That is a large combinatorial surface. Do not approve “we turned on waffle” without naming the layout, shape, and update behavior you will support.

TypeScript accepting 'unit' | 'waffle' is helpful. It is not a visual test. A green tsc run does not prove the watermark is gone, that legend toggles reflow marks correctly, or that a 10×10 percentage waffle still reads at mobile width.

Free chart path versus premium unit and waffle path

Gate 3 — Fixtures beat demo pages

Release notes ship demos. Products ship contracts. Build fixtures that encode the decisions you refuse to rediscover later.

 1// tests/fixtures/apex-unit-contract.ts
 2export const energyMixFixture = {
 3  id: "energy-mix-waffle-100",
 4  chartType: "waffle" as const,
 5  layout: "grid",
 6  grid: { total: 100, columns: 10, fillFrom: "bottom" as const },
 7  series: [35, 23, 15, 9, 8, 6, 4],
 8  labels: ["Coal", "Gas", "Hydro", "Nuclear", "Wind", "Solar", "Other"],
 9  // Expected after largest-remainder allocation to 100 cells — assert in visual or unit harness
10  expectedCellSum: 100,
11  fallbackType: "donut" as const,
12  requiresLicense: true,
13};
14
15export const adoptionChecklist = [
16  "license mode screenshot (trial vs licensed)",
17  "import path uses apexcharts/unit",
18  "layout and shape pinned in fixture",
19  "legend hide/show reflow checked",
20  "narrow width readability checked",
21  "keyboard/ARIA smoke checked",
22  "wrapper path tested if vue3-apexcharts is used",
23  "SSR/hydration path tested if Nuxt or equivalent is used",
24  "fallback free chart type renders with same labels/series",
25  "rollback PR description names the free type and version pin",
26] as const;

For percentage waffles, the docs state that grid.total: 100 uses a largest-remainder rule so cells sum exactly to the budget. That is a perfect automated assertion target. For grid.split: true, each tile is a mini-waffle with its own track — assert tile count and labels, not only total series length.

 1# scripts/check-apex-unit-usage.sh
 2# Fail CI if premium types appear without the unit import in the same package graph area.
 3set -euo pipefail
 4
 5if rg -n "type:\\s*['\"]waffle['\"]|type:\\s*['\"]unit['\"]" resources/js apps --glob '!**/node_modules/**'; then
 6  if ! rg -n "apexcharts/unit" resources/js apps --glob '!**/node_modules/**'; then
 7    echo "unit/waffle usage found without apexcharts/unit import" >&2
 8    exit 1
 9  fi
10fi

Keep the fallback cheap. If unit or waffle is rejected for license, density, or accessibility reasons, the same labels and series should render as a donut or bar without a second product design cycle.

Gate 4 — Accessibility and density are first-class

Discrete marks sell “countability.” They also change the failure modes:

  • A 100-cell waffle is readable when categories are few and proportions are coarse.
  • The same waffle becomes noise when you push ten near-equal categories or update every second.
  • Pictogram/image marks can fail contrast and meaning when icons are decorative rather than encoded.
  • Legend click reflow and animated transitions can be motion-heavy; respect reduced-motion settings in your app shell even if the chart demo celebrates tweening.

ApexCharts documents keyboard navigation and ARIA support at the library level on npm marketing copy. That is not a certificate for your specific unit layout. Run a short manual harness:

  1. Tab to the chart region and any focusable controls your wrapper exposes.
  2. Toggle a legend item if the chart uses one; confirm the remaining marks still make sense.
  3. Capture the chart at the narrowest supported admin width.
  4. Compare against the free fallback type with the same data.

If the chart only works as a large decorative panel, it does not belong in a dense operations table view. Put it on an analytics page with room, or keep the free chart.

Layout shortlist for first production use

Start with one layout, not six.

  • waffle / grid + total 100 — part-to-whole percentages; easiest story for stakeholders.
  • unit + grouped — category comparison with countable dots; good for small integer KPIs.
  • Defer scatter / beeswarm / image pictograms until you have fixtures for axes, packing determinism, and icon contrast.

The release lists all six layouts; your product does not need to support all six on day one.

Gate 5 — Wrapper and SSR are separate version facts

vue3-apexcharts remains 1.11.1 on npm at collection time (last publish timestamp in 2026-03). Core moving to 6.6.x does not prove the Vue wrapper, Nuxt plugins, or your client-only boundaries understand premium chart types.

Test in this order:

  1. Direct apexcharts + import 'apexcharts/unit' on a plain page.
  2. The same config through vue3-apexcharts if that is how production mounts charts.
  3. SSR route render + client hydration if you use Nuxt or another meta-framework.
  4. Navigation away/back, resize, and teardown.
 1<!-- resources/js/components/EnergyMixWaffle.vue -->
 2<script setup lang="ts">
 3import { onBeforeUnmount, onMounted, ref } from "vue";
 4import type ApexCharts from "apexcharts";
 5import { renderEnergyWaffle, type MixSlice } from "../charts/unit-chart";
 6
 7const props = defineProps<{ slices: MixSlice[] }>();
 8const host = ref<HTMLElement | null>(null);
 9let chart: ApexCharts | null = null;
10
11onMounted(() => {
12  if (!host.value) return;
13  chart = renderEnergyWaffle(host.value, props.slices);
14});
15
16onBeforeUnmount(() => {
17  chart?.destroy();
18  chart = null;
19});
20</script>
21
22<template>
23  <div
24    ref="host"
25    class="energy-mix-waffle"
26    role="img"
27    :aria-label="`Energy mix waffle for ${slices.length} categories`"
28  />
29</template>

If the wrapper lags or hydration flickers, keep the free chart on SSR routes and load unit/waffle only after client mount with a skeleton that does not claim false precision.

This is the same separation used when ApexCharts 6.4 changed heatmap contracts: core release notes define core behavior; wrapper compatibility is an independent check. See the earlier maintenance write-up on the ApexCharts 6.4 dashboard contract.

Version facts for core, npm, wrapper, and SSR

How this differs from the 6.4 dashboard contract

The 6.4 work was about heatmap geometry, Canvas imports, and default interaction drift on charts many teams already shipped. The 6.6 work is about introducing a paid visualization path.

Dimension6.4 theme6.6 unit/waffle theme
Primary riskSilent visual/behavior drift on existing heatmapsWatermark, license, and product approval on a new type
Import concernOptional Canvas renderer feature importRequired apexcharts/unit for tree-shakeable builds
Default posturePreserve or accept new heatmap defaultsDefault remain on free types until gates pass
RollbackRestore options / previous renderer pathSwap back to pie/donut/bar with same series
Success signalScreenshots and interaction tests match the chosen contractLicensed screenshot + fixture + free fallback both green

Keep both posts in the maintenance index. They are related and not duplicates.

What good rejection looks like

Not every shiny chart type should merge. A successful evaluation can end with “no.”

Reject unit/waffle when:

  • Finance will not approve premium packaging for the surfaces that need it.
  • The watermark appears in any shared environment under the current key injection design.
  • The data updates too fast for discrete mark transitions to stay readable.
  • Accessibility review fails contrast, motion, or keyboard checks.
  • The free fallback already answers the user question with less operational risk.

Write the rejection into the same doc as an approval would use. Future you will thank present you when someone pastes a Dribbble waffle and asks why the app still uses a donut.

What you should do Monday morning

  1. Read the primary v6.6.0 release notes and the waffle chart guide. Copy the claims you care about into the PR description with links.
  2. Record the installed versions: apexcharts (likely 6.6.0 or 6.6.1 from npm) and vue3-apexcharts (currently 1.11.1 if unchanged). Note that 6.6.1 has no GitHub release body at the time of writing.
  3. Decide the product rule: premium chart types allowed only on named routes, or frozen until license review completes.
  4. Implement license configuration once, client-side, before render. Capture trial vs licensed screenshots for the same fixture.
  5. Add import 'apexcharts/unit' only in the entry path that mounts unit/waffle. Add a CI grep so premium types cannot ship without that import.
  6. Create one fixture with real labels from a production KPI. Pin layout, grid totals, and the free fallback type.
  7. Run direct ApexCharts tests, then wrapper tests, then SSR/hydration if applicable.
  8. Perform a 15-minute accessibility and density pass at the narrowest supported width.
  9. Only then open the design/product review with screenshots of licensed mode and the free fallback side by side.
  10. File the decision under your developer tools notes and link related dashboard maintenance from start here so the next upgrade does not restart from a lockfile diff.

Further reading

  • Source

  • Source

  • Source

  • Source

Related on this site: ApexCharts 6.4 dashboard contract, Laravel + Vue SaaS, developer tools, and AI agent operations for the same change-control instinct applied to agents rather than chart types.