@unovis/ts
Unovis core package for vanilla TypeScript/JavaScript. Version 1.7.0, Apache-2.0,
ES modules only ("type": "module"). Renders SVG (plus Leaflet/MapLibre for maps)
into a DOM element. Source paths cited below are relative to the package root
(node_modules/@unovis/ts/).
Install (per README.md and https://unovis.dev/docs/intro):
npm install -P @unovis/ts
TypeScript setup
- Enable
allowSyntheticDefaultImportsincompilerOptions(required by Unovis dependencies). Source: https://unovis.dev/docs/intro - If
tsconfig.jsonliststypesexplicitly, add"topojson-client"to it. Source: https://unovis.dev/docs/intro - Styles are injected at runtime via
@emotion/css(declared inpackage.jsonsideEffects:styles/index.js). Importing any component injects the global CSS variables. Do not import a CSS file; there is none. For strict CSP sites read https://unovis.dev/docs/guides/csp
Architecture
Three building blocks (README.md, index.js):
- Containers own the DOM element, the data, size, and rendering.
XYContainer(element, config, data)— multiple XY components plusxAxis,yAxis,tooltip,crosshair,annotationsslots (containers/xy-container/config.d.ts).SingleContainer(element, config, data)— exactly one component (componentslot), e.g. Graph, Sankey, TopoJSONMap, Donut, Treemap (containers/single-container/config.d.ts).
- Components are constructed with their config:
new Line({ x, y }). They hold no data; the container feeds them the data array. - Standalone components need no container:
LeafletMap,LeafletFlowMap, and the legends (BulletLegend,RollingPinLegend,FlowLegend). Source: https://unovis.dev/docs/intro
Data records are generic (Line<Datum>), read through accessors:
a value or a function (d, i) => value. Types in types/accessor.d.ts:
NumericAccessor, StringAccessor, ColorAccessor, BooleanAccessor,
GenericAccessor.
import { Axis, Line, XYContainer } from '@unovis/ts'
type DataRecord = { x: number; y: number }
const data: DataRecord[] = [
{ x: 0, y: 0 },
{ x: 1, y: 2 },
{ x: 2, y: 1 },
]
const line = new Line<DataRecord>({
x: d => d.x,
y: d => d.y,
})
const chart = new XYContainer(document.getElementById('vis-container')!, {
components: [line],
xAxis: new Axis(),
yAxis: new Axis(),
}, data)
Individual imports are tree-shakable and equivalent
(@unovis/ts/containers/xy-container, @unovis/ts/components/line, ...).
Source: https://unovis.dev/docs/quick-start. Heavy parts (Graph layouts,
LeafletMap renderer) load via dynamic import after initialization.
Update lifecycle
Data lives on the container; configs live on components. Update through the
container's public API (containers/xy-container/index.d.ts):
| Method | Purpose |
|---|---|
setData(data, preventRender?) | Replace the data array |
updateContainer(config, preventRender?) | Merge container config |
updateComponents(configs[], preventRender?) | Merge configs, index-aligned with components |
update(containerConfig, componentConfigs?, data?) | All at once |
SingleContainer has updateComponent (singular) instead of
updateComponents (containers/single-container/index.d.ts). Pass
preventRender: true to defer rendering when batching updates.
Always call chart.destroy() when tearing down (SPA route change, test
teardown). It removes the ResizeObserver, DOM node, and timers
(core/container/index.d.ts).
Sizing
Container config width/height accept pixels or CSS units but not %
(core/container/config.d.ts). For percentage sizing, set width/height
via CSS on the container's DOM element instead. sizing controls fit
behavior: Sizing.Fit (default), Sizing.Extend, Sizing.FitWidth
(types/component.d.ts).
Common recipes
Tooltip and crosshair go in the container config, not the components array:
import { Axis, Crosshair, Line, Tooltip, XYContainer } from '@unovis/ts'
const tooltip = new Tooltip()
const crosshair = new Crosshair<DataRecord>({
color: d => d.color ?? null,
template: d => `<div>${d.x}: ${d.y}</div>`,
})
const chart = new XYContainer(el, {
components: [line],
xAxis: new Axis(),
yAxis: new Axis(),
tooltip,
crosshair,
}, data)
Stacked bars use one y accessor per stack level:
import { Axis, StackedBar, XYContainer } from '@unovis/ts'
const bar = new StackedBar<DataRecord>({
y: [d => d.a, d => d.b],
})
new XYContainer(el, { components: [bar], xAxis: new Axis(), yAxis: new Axis() }, data)
A graph lives in a SingleContainer with { nodes, links } data:
import { Graph, GraphLayoutType, SingleContainer } from '@unovis/ts'
const graph = new Graph<GraphNodeDatum, GraphLinkDatum>({
layoutType: GraphLayoutType.Force,
linkStroke: l => l.color ?? null,
nodeSize: n => n.size ?? 10,
})
const chart = new SingleContainer(el, { component: graph }, {
nodes: [{ id: 'a' }, { id: 'b' }],
links: [{ source: 'a', target: 'b' }],
})
Events are declared per-component with CSS selectors exposed as static
selectors on each component class (core/component/config.d.ts):
const area = new Area<DataRecord>({
x: d => d.x,
y: d => d.y,
events: {
[Area.selectors.area]: {
click: d => console.log('clicked', d),
},
},
attributes: { // stable hooks for E2E tests
[Area.selectors.area]: { 'test-value': d => String(d.y) },
},
})
Map with bundled TopoJSON (maps.d.ts exports WorldMapTopoJSON,
USATopoJSON, GermanyTopoJSON, UKTopoJSON, FranceTopoJSON,
IndiaTopoJSON, ChinaTopoJSON, USCountiesTopoJSON,
WorldMapSimplestTopoJSON, WorldMap110mAlphaTopoJSON):
import { SingleContainer, TopoJSONMap, WorldMapTopoJSON } from '@unovis/ts'
const map = new TopoJSONMap<AreaDatum, PointDatum>({
topojson: WorldMapTopoJSON,
mapFeatureName: 'countries',
areaColor: d => d.color ?? null,
pointColor: d => d.color ?? null,
longitude: d => d.longitude ?? null,
latitude: d => d.latitude ?? null,
})
new SingleContainer(el, { component: map }, areaData)
Rules and pitfalls
- Do not mutate
component.configorcontainer.configdirectly; call theupdate*methods so changes merge with defaults and re-render. Axisinstances forxAxis/yAxisneed notype; the container assigns it (containers/xy-container/config.d.ts). Settypeonly for a standaloneAxisincomponents.- Colors: component
coloraccessors returningnullfall back to the container palette (--vis-color<N>CSS variables). UsecolorKeyson XY components to map series to colors (core/xy-component/config.d.ts). - Animation duration: container
durationoverrides all components; componentduration(default600) applies to data transitions. - Numeric string enums (
CurveType,SymbolType,Position, ...) also accept their raw string values, e.g.curveType: 'monotoneX'. - Package targets browsers. Node usage needs a DOM shim (the library's own
tests run Vitest with jsdom,
package.jsonscripts).
References
- API surface — full export catalog, container and component class API, events/attributes, tooltips.
- Components — per-component key configs, defaults, and data shapes.
- Styling and theming — color palette, CSS variables, dark theme, patterns, testing.
Official documentation: https://unovis.dev/docs/intro — component reference at
https://unovis.dev/docs/components/<Name> (e.g. /docs/components/Line),
containers at /docs/containers/XY_Container and
/docs/containers/Single_Container. Gallery: https://unovis.dev/gallery.
Release notes: https://github.com/f5/unovis/releases (current prepared
version 1.7.0, package.json).