Englische Referenzdokumentation. Der technische Originaltext und die Codebeispiele bleiben unverändert auf Englisch; die Website-Navigation ist deutsch.

Getting started

Install the public MIT Core, use a pinned browser CDN or offline archive. ES modules, CommonJS, React, Vue, Svelte, Angular, SSR, TypeScript and CSP.

View .md Agent setup

WestCoast Charts draws interactive SVG charts for dashboards and web apps.

Install

Install the Free Core for version 1.1.5. It includes the MIT Core and the Full Free bundle, all free chart types and features, optional validation/describe helpers and schemas. No account, payment or licence key is required. Premium is supplied separately through the 30-day trial or a paid licence; its imports require that separately supplied complete package.

Start with Core; add only the extras you need

For a basic line, bar or pie chart, start with the standard Core entry. Full Free also loads all free optional types and features. Install the pinned version, then import Core:

sh
npm install west-coast-charts@1.1.5
js
import WestCoastCharts from 'west-coast-charts'

const chart = new WestCoastCharts(document.querySelector('#chart'), {
  chart: { type: 'line', height: 320 },
  series: [{ name: 'Requests', data: [12, 19, 15] }],
  xaxis: { categories: ['09:00', '10:00', '11:00'] }
})
await chart.render()

For an optional type, register its module before rendering. A dynamic import can defer it until its view opens:

js
async function showGauge(element, options) {
  const { default: gauge } = await import('west-coast-charts/types/gauge')
  WestCoastCharts.use(gauge)
  const chart = new WestCoastCharts(element, options)
  await chart.render()
  return chart
}
Released browser build File Gzip level 9
Standard Core west-coast-charts.min.js 194,836 B
Extra types west-coast-charts-types.min.js 83,472 B
Full Free west-coast-charts.full.min.js 380,285 B

These sizes describe compressed artifacts, not a loading-time or interaction-speed guarantee. Core already contains the standard renderers; /core aliases the normal entry, so selective optional imports do not remove that baseline. Extra types is an optional module, not another complete Core bundle. Shared runtime code contributes to each optional type's actual cost. See measured consumer sizes for every optional type and the reproducible method.

Heatmap row order and Gauge option conventions explain two configuration choices before you use them.

Public Core distribution

sh
npm install west-coast-charts@1.1.5
  • Archive SHA-256: c4a4fdfebe81f29ad27b6e583e3a343e79e93633f54208f0c26a6d586a577dcc.
  • Pinned browser CDN bundle. Load it as a script tag, without a bundler. Use the release tag, not an unversioned latest URL.
  • Core includes optional agent/validate, agent/describe and versioned JSON schemas. Install and register these modules explicitly when using the lean Core entry.
  • A cloud workspace restricted to GitHub and npm can install the GitHub archive. Its artifact viewer may have a separate network allowlist: bundle the installed library into the artifact if it cannot load the CDN. Publishing a package cannot override a client's network policy.

Offline archives

  • Core ZIP: unzip it and open example.html for an offline browser example.
  • Core npm archive: install the downloaded file in your application.
sh
npm install ./west-coast-charts-1.1.5.tgz

The command above installs the downloaded file. Use the verified public installation channel above when the website download host is unavailable in your workspace.

For a page without a bundler, copy the ZIP's dist folder alongside your HTML:

html
<div id="chart"></div>
<script src="./dist/west-coast-charts.full.min.js"></script>
<script>
  new WestCoastCharts(document.querySelector('#chart'), {
    chart: { type: 'bar', height: 300 },
    series: [{ name: 'Deploys', data: [12, 19, 7, 15, 22] }],
    xaxis: { categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'] }
  }).render()
</script>

The Full Free bundle includes the free plugins. With the lean Core entry, register only the additional types and features your application needs, as shown below. The library injects its stylesheet when the first chart renders.

Browser CDN

Load the versioned Full Free bundle directly. Its Subresource Integrity hash checks the exact bytes; allow cdn.jsdelivr.net in your application's script-src policy if you use it.

html
<script src="https://cdn.jsdelivr.net/npm/west-coast-charts@1.1.5/dist/west-coast-charts.full.min.js" integrity="sha384-IvVUi5Mw5BSFzQ14ISAoMnSHE1Ot+DIoyMCM5DHL/TEsZuw0l2j5im+MGc7d8C0A" crossorigin="anonymous"></script>

If an artifact viewer blocks this CDN, bundle the installed Core into your artifact instead. That viewer's network policy is outside the library's control.

Your first chart

js
import WestCoastCharts from 'west-coast-charts'

const chart = new WestCoastCharts(document.querySelector('#chart'), {
  chart: { type: 'line', height: 320 },
  series: [
    { name: 'p95 latency (ms)', data: [210, 198, 240, 380, 260, 220, 205] },
    { name: 'p50 latency (ms)', data: [88, 85, 92, 140, 101, 90, 87] },
  ],
  xaxis: { categories: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'] },
  title: { text: 'Checkout latency' },
})
await chart.render()

// later: new data animates from the current state
await chart.updateSeries([
  { name: 'p95 latency (ms)', data: [205, 190, 230, 300, 250, 215, 200] },
  { name: 'p50 latency (ms)', data: [86, 84, 90, 120, 98, 88, 86] },
])

// when the element goes away
chart.destroy()

Three things every chart needs:

  1. An element. The chart fills its width; set chart.height (or a CSS height on the element).
  2. Options. chart.type, series and, for category axes, xaxis.categories. The full list is in the options reference.
  3. render(). It returns a promise that resolves once the chart is drawn.

Every chart is accessible without extra work: keyboard navigation, screen-reader descriptions and a hidden data table are on by default. See Accessibility reference.

Entries: core, types, features, premium

The package is split so you only ship what you use.

Import What you get License
west-coast-charts The core: line, area, bar, pie/donut, radialBar, scatter, bubble, heatmap, candlestick, boxPlot, radar, polarArea, rangeBar, rangeArea, treemap, plus theming, accessibility, animation and export MIT
west-coast-charts/types All additional chart types: gauge, funnel/pyramid, waterfall, dumbbell, histogram, violin/raincloud, sunburst, icicle, streamgraph, unit/waffle, barRace, calendar, slope, beeswarm, nested treemaps, extra theme presets MIT
west-coast-charts/types/<name> One of those types (side-effect import), e.g. west-coast-charts/types/gauge MIT
west-coast-charts/features/<name> One optional feature: drilldown, link, highlight-filter, history, narrative, perspectives, storyboard, trellis, readability, renderer-canvas MIT
west-coast-charts/full Core + all types + all features in one file MIT
west-coast-charts/premium Premium chart types: sankey, chord, gantt, tree, stock, map, marimekko, network, radialColumn. Needs a licence key in production, see Premium and licensing Commercial key (code MIT)

Plugins are installed with WestCoastCharts.use(). Installing the same plugin twice does nothing.

js
import WestCoastCharts from 'west-coast-charts'
import types from 'west-coast-charts/types'

WestCoastCharts.use(types)

const chart = new WestCoastCharts(document.querySelector('#chart'), {
  chart: { type: 'waterfall', height: 300 },
  series: [{ name: 'Monthly cost (EUR)', data: [
    { x: 'Start', y: 4200 }, { x: 'Compute', y: 900 }, { x: 'Storage', y: 250 },
    { x: 'Savings plan', y: -700 }, { x: 'Egress', y: 180 },
  ] }],
})
await chart.render()

If a chart uses a type whose entry is not loaded, render() rejects with a message that names the import to add, and the same hint is logged once. Nothing is silently drawn as a different type.

ES modules, CommonJS and script tags

The package has an exports map, so bundlers (Vite, webpack 5, Rollup, esbuild, Parcel) and Node pick the right file automatically.

Format Core Premium
ES module (import) dist/west-coast-charts.esm.js dist/west-coast-charts-premium.esm.js
CommonJS (require) dist/west-coast-charts.common.js dist/west-coast-charts-premium.common.js
Script tag (UMD, global WestCoastCharts) dist/west-coast-charts.min.js (.js = unminified) dist/west-coast-charts-premium.min.js (global WestCoastChartsPremium)
AMD (UMD build) dist/west-coast-charts.min.js (define.amd supported) –

ES modules:

js
import WestCoastCharts from 'west-coast-charts'
// the same class under its compatibility name:
import { ApexCharts } from 'west-coast-charts'

console.log(ApexCharts === WestCoastCharts) // true

CommonJS:

js
const WestCoastCharts = require('west-coast-charts')
const premium = require('west-coast-charts/premium')

WestCoastCharts.use(premium)

Script tags: plugin bundles loaded after the core install themselves into the global WestCoastCharts. Calling use() yourself as well is harmless. Feature bundles (dist/features/<name>.min.js) share a small runtime: load dist/features/runtime.min.js once, right after the core and before the first feature bundle (west-coast-charts.full.min.js already contains it). Bundlers resolve this automatically.

html
<div id="chart"></div>
<script src="dist/west-coast-charts.min.js"></script>
<script src="dist/west-coast-charts-types.min.js"></script>
<script src="dist/west-coast-charts-premium.min.js"></script>
<script>
  new WestCoastCharts(document.querySelector('#chart'), {
    chart: { type: 'sankey', height: 300 },
    series: [{ data: [
      { from: 'Ingress', to: 'API', value: 70 },
      { from: 'Ingress', to: 'Static', value: 30 },
      { from: 'API', to: 'Postgres', value: 45 },
      { from: 'API', to: 'Cache', value: 25 },
    ] }],
  }).render()
</script>

(This page is served from a public host name and has no licence key, so the premium chart shows the small "unlicensed" line. See Premium and licensing.)

Frameworks

WestCoast Charts has no framework dependency and there are no official wrapper packages yet. A chart needs three lifecycle steps, which map onto every framework the same way:

Step Call
element is in the DOM chart = new WestCoastCharts(el, options); chart.render()
options change chart.updateOptions(options) (or chart.updateSeries(series) for data only)
element is removed chart.destroy()

The examples below load the library with a dynamic import() inside the mount step. That keeps the library out of the server bundle in Next.js, Nuxt, SvelteKit and Angular SSR (see Server-side rendering) and splits it into its own chunk. In a client-only app a normal top-level import works just as well.

React

jsx
'use client' // Next.js app router: render on the client only
import { useEffect, useRef } from 'react'

export default function Chart({ options }) {
  const el = useRef(null)
  const chart = useRef(null)

  // create once, destroy on unmount
  useEffect(() => {
    let alive = true
    import('west-coast-charts').then(({ default: WestCoastCharts }) => {
      if (!alive) return
      chart.current = new WestCoastCharts(el.current, options)
      chart.current.render()
    })
    return () => {
      alive = false
      chart.current?.destroy()
      chart.current = null
    }
  }, [])

  // later option changes animate the existing chart
  useEffect(() => {
    chart.current?.updateOptions(options)
  }, [options])

  return <div ref={el} />
}

Keep options stable between renders (useMemo, state, or a module constant); a new object on every render means an update on every render.

Vue 3

vue
<script setup>
import { onBeforeUnmount, onMounted, ref, toRaw, watch } from 'vue'

const props = defineProps({ options: { type: Object, required: true } })
const el = ref(null)
let chart = null
let alive = true

onMounted(async () => {
  const { default: WestCoastCharts } = await import('west-coast-charts')
  if (!alive) return
  chart = new WestCoastCharts(el.value, toRaw(props.options))
  chart.render()
})

watch(() => props.options, (options) => chart?.updateOptions(toRaw(options)), { deep: true })

onBeforeUnmount(() => {
  alive = false
  chart?.destroy()
})
</script>

<template>
  <div ref="el" />
</template>

Svelte 5

svelte
<script>
  import { onMount } from 'svelte'

  let { options } = $props()
  let el
  let chart = $state(null)

  onMount(() => {
    let alive = true
    import('west-coast-charts').then(({ default: WestCoastCharts }) => {
      if (!alive) return
      chart = new WestCoastCharts(el, $state.snapshot(options))
      chart.render()
    })
    return () => {
      alive = false
      chart?.destroy()
    }
  })

  // runs again whenever `options` changes
  let first = true
  $effect(() => {
    const next = $state.snapshot(options)
    if (!chart) return
    if (first) { first = false; return } // the chart was just created with these options
    chart.updateOptions(next)
  })
</script>

<div bind:this={el}></div>

Angular (17+)

ts
import { Component, ElementRef, Input, OnChanges, OnDestroy, ViewChild, afterNextRender } from '@angular/core'
import type { WestCoastChartsOptions } from 'west-coast-charts'

@Component({
  selector: 'wcc-chart',
  standalone: true,
  template: '<div #host></div>',
})
export class ChartComponent implements OnChanges, OnDestroy {
  @Input({ required: true }) options!: WestCoastChartsOptions
  @ViewChild('host', { static: true }) host!: ElementRef<HTMLDivElement>
  private chart: any = null
  private destroyed = false

  constructor() {
    // afterNextRender runs in the browser only, never during server-side rendering
    afterNextRender(async () => {
      const { default: WestCoastCharts } = await import('west-coast-charts')
      if (this.destroyed) return
      this.chart = new WestCoastCharts(this.host.nativeElement, this.options)
      this.chart.render()
    })
  }

  ngOnChanges(): void {
    this.chart?.updateOptions(this.options)
  }

  ngOnDestroy(): void {
    this.destroyed = true
    this.chart?.destroy()
  }
}

For Angular 16 and older, create the chart in ngAfterViewInit and guard it with isPlatformBrowser(platformId) when you use server-side rendering.

Other frameworks

Anything with a "mounted" and an "unmounted" hook works the same way (Solid onMount/onCleanup, Lit firstUpdated/disconnectedCallback, Alpine x-init, plain web components connectedCallback/ disconnectedCallback).

Server-side rendering

Importing the library on the server is safe: loading it does not touch window or document, so a module that imports it can be part of a server bundle (Next.js, Nuxt, SvelteKit, Angular SSR, plain Node). Drawing a chart still needs a browser DOM. Create charts in a client-only step (useEffect, onMounted, onMount, afterNextRender), as the framework examples above do.

js
// runs in plain Node: no window, no document
import WestCoastCharts from 'west-coast-charts'

console.log(typeof window)                          // 'undefined'
console.log(WestCoastCharts.escapeHtml('<b>Q1</b>')) // helpers that need no DOM work on the server

The dynamic import() in the framework examples is therefore not required for correctness; it keeps the library out of the server bundle and out of the first page load.

Server rendering of charts to static SVG is not part of v1. Calling new WestCoastCharts(...) or render() without a DOM fails. Tests and scripts that use a DOM implementation in Node (jsdom, happy-dom) can create the DOM before or after the import; the chart constructor picks up the current window. Charts measure text and SVG geometry, which those DOM implementations do not provide, so expect stubs for getBBox / getComputedTextLength there (the library's own unit tests use the ones in tests/unit/__mocks__/SVGGeometry.js).

TypeScript

Type declarations ship with the package; there is nothing extra to install.

ts
import WestCoastCharts, { type WestCoastChartsOptions } from 'west-coast-charts'

const options: WestCoastChartsOptions = {
  chart: { type: 'area', height: 280 },
  series: [{ name: 'CPU %', data: [31, 40, 28, 51, 42, 109, 100] }],
}
new WestCoastCharts(document.getElementById('chart'), options).render()

ApexOptions and every Apex* interface are still exported, so typed code written for ApexCharts 4.x compiles unchanged. Premium options are typed in west-coast-charts/premium (see Premium options reference).

Content Security Policy

  • Styles. The library injects <style> elements. With a nonce-based style-src, pass the nonce: chart: { nonce: '...' }. The charts also set inline style attributes, so allow style-src-attr 'unsafe-inline'.
  • Trusted Types. Pages that enforce Trusted Types must allow the policy name west-coast-charts.
  • PNG export. Rasterising uses a blob: URL and falls back to data:; img-src must allow one of them for PNG export.

More in Performance and security.

Next steps

WestCoast Charts 1.1.5 · generated from the library guide at commit 95f093ba. Code examples in the guide are executed by the library's docs check.