Skip to content

Chart Customization

Every built-in native chart uses the same renderer customization contract. This applies to all 68 native types, whether created through a per-type factory, the full catalog, or Pivot. Use chart state for saved settings and renderer options for application callbacks and branding.

DetailAPI
Default series/category colorsrendererOptions.palette
Stable semantic colors, member/measure colors, gradients, conditional colorscolors
Series color, visibility, axis, mark type, stacking, errorsseriesOverrides[seriesId] (supported fields depend on the chart family)
Chart background, foreground, muted text, gridrendererOptions.theme (tokens or a callback)
Titles, description, legend placement, data labelspresentation
Every SVG label’s content, visibility, typography, class, accessible namerendererOptions.labels.styles and labels.formatter
Individual marks, fills, outlines, dashes, opacity and typographyrendererOptions.elements.styles and elements.formatter
Tooltips and their contentrendererOptions.tooltip, including formatter, className and timing
Domains, scales, tick formats, units, titlesaxes
Reference lines, ranges, text and drawing shapesannotations
Family-specific geometrychartOptions; inspect the selected catalog definition’s schema
Arbitrary SVG geometry, custom layers and remaining visual detailsrendererOptions.onRender
UI strings and number/date formattinglocaleText

Model properties remain subject to the selected chart’s capabilities and validation. For example, axis-free charts have no numeric axis to configure. Renderer style callbacks provide visual overrides across all native types; they do not add new analytical capabilities.

import { createLineChart } from '@revolist/charts/line';
const chart = await createLineChart({
container,
data: [{ month: 'Jan', revenue: 120 }, { month: 'Feb', revenue: 180 }],
mapping: {
category: { field: 'month' },
series: [{ field: 'revenue', label: 'Revenue' }],
},
presentation: { title: 'Monthly revenue', dataLabels: true },
rendererOptions: {
palette: ['#6647f0', '#fd7e14'],
fontFamily: 'Inter, system-ui, sans-serif',
theme: { background: '#ffffff', foreground: '#172033', grid: '#e2e8f0' },
labels: {
styles: {
title: { fontSize: 20, fontWeight: 700 },
data: { fill: '#172033', fontSize: 12 },
},
formatter(context) {
if (context.kind === 'data' && context.value != null) {
return { text: `€${context.value}`, ariaLabel: `${context.value} euros` };
}
},
},
elements: {
styles: { mark: { strokeWidth: 2, strokeLinejoin: 'round' } },
formatter(context) {
if (context.kind === 'mark' && context.value != null && context.value > 150) {
return { fill: '#0f766e', stroke: '#115e59' };
}
},
},
onRender({ svg, bounds }) {
const note = svg.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'text');
note.setAttribute('x', String(bounds.left + bounds.width));
note.setAttribute('y', '16');
note.setAttribute('text-anchor', 'end');
note.setAttribute('fill', '#64748b');
note.setAttribute('font-size', '10');
note.textContent = 'Internal reporting';
svg.append(note);
},
},
});

rendererOptions configures the factory’s native renderer. Supply either it or an explicit renderer; combining both rejects before rendering. A custom renderer is responsible for its own customization contract. A palette colors the family-defined series or category channel; use colors when identities must remain stable across ordering changes.

For a small custom native renderer bundle, the equivalent is createNativeChartsRenderer([lineChart], options) from @revolist/charts/core. For Pivot, pass the same options to createPivotChartsRenderer(options) from @revolist/pivot/charts and install that renderer in the chart plugin configuration.

labels.styles keys are title, subtitle, axisTitle, axisTick, category, legend, data, annotation, summary and specialized. labels.formatter receives chartId, chartType, renderMode, kind, defaultText, and available axis/category/series/value/annotation metadata. Return a string to replace text, a ChartLabelResult to set text/style/classes/accessibility, null or { hidden: true } to suppress a label, or undefined to keep it. Use this API for typography so native heading and label layout can measure the authored text.

elements.styles keys are:

  • background: the chart’s background rectangle.
  • mark: datum, inspection or mark-group graphics, including grouped decorative children.
  • label: all SVG text. labelKind identifies its semantic label role.
  • legend: non-text graphics within legend items or overflow controls.
  • annotation: non-text graphics with an annotation identity.
  • decoration: remaining visible SVG graphics, such as grids, axes, paths and navigation graphics.

elements.formatter receives the chart ID/type, render mode, role, SVG tagName, original attributes, and available canonical categoryIndex, seriesIndex, category, series and value. Series identities remain canonical when hidden/helper series are omitted from geometry. Aggregate or decorative visuals may have no individual datum. Use attributes to target a chart-specific graphic that shares a role, such as a line path or a gauge track. The original attributes describe the built-in result before element styling, including label customization.

Styles support fill, stroke, fill/stroke opacity, stroke width/dashes/offset/caps/joins, fill rule, vector effect, paint order, overall opacity and the ChartLabelStyle typography properties. Shared kind styles apply first; the element formatter wins for properties it supplies. Element styles override built-in presentation, chart options and label styles; onRender runs last. Gradients and patterns authored through the model remain intact unless their fill is explicitly overridden.

SVG definitions, clipping paths, masks, patterns, markers and the plot-bounds measurement rectangle are excluded from element styling. Groups are not styled a second time, avoiding multiplied inherited opacity. To alter definitions or geometry, use onRender.

Both style callbacks and onRender run synchronously on each complete SVG paint: creation, model/data updates, resize, category scrolling and viewport navigation. Callbacks also run in previews; inspect renderMode when needed. Lightweight peer-hover and status-only updates do not repaint the whole chart.

onRender receives { svg, input, bounds, theme }, with the canonical input including hidden series. Mutate only the supplied chart SVG. Custom SVG children are removed on the next complete paint, so append them again on every invocation. Keep callbacks deterministic, synchronous and free of chart-update calls; treat the input, theme and bounds as read-only. The chart owns its interaction targets and accessibility, so preserve their event handlers, identity attributes and focus semantics when changing geometry.

SVG, PNG and PDF exports use the customized SVG. SVG additions must be self-contained for portable export. Image/PDF encoder limitations still apply. Runtime callbacks and renderer options are not serialized in chart.model: reapply them when restoring a model. To change dynamic branding, close over application settings and trigger an ordinary chart.update({}); callbacks use the latest settings on that repaint. To change renderer-wide configuration objects, create a new renderer/chart.

import { CHART_DEFINITIONS } from '@revolist/charts/catalog';
const definition = CHART_DEFINITIONS.find(({ type }) => type === chart.model.chartType)!;
console.table(definition.options.fields);
// Each field includes its key, control, default and numeric range or select values.
// definition.options.defaults contains the complete defaults for this type.

Use those exact keys in chartOptions. The schema is authoritative for that chart’s built-in controls. Common family controls include bar corner radius and padding, line stroke width, area fill opacity, radial angles/radii, histogram bins, financial price styles, gauge domains/segments and facet scale mode. See capability coverage for the family-specific contracts.

Geographic charts have a separate provider lifecycle. All three types (choropleth, proportionalSymbolMap, geographicHeatmap) accept rendererOptions on the pack.render(input)/instance.update(input) input:

const instance = pack.render({
container, request, localeText,
rendererOptions: {
theme: { background: '#f8fafc', foreground: '#172033' },
elements: {
styles: { mark: { fill: '#0f766e', stroke: '#ffffff', strokeWidth: 1 } },
formatter: ({ kind, value }) => kind === 'mark' && value != null && value < 0
? { fill: '#b91c1c' } : undefined,
},
onRender({ svg, data }) {
svg.setAttribute('aria-label', `Map: ${data.projectionId}`);
},
},
});
await instance.ready;

Geographic renderer options include className, fontFamily, theme tokens, the shared element styles/formatter and onRender({ svg, input, data, theme }). Marks expose category/series IDs when supplied by the provider. Locale functions format geographic values and accessible labels; onRender can replace visible attribution text or add data labels. The hook runs after a successful provider paint, including updates and provider-owned overlays. Projection, paths and positions remain provider-owned.

The optional ECharts Canvas adapter has a separate supported-feature contract. Native SVG element callbacks do not apply to Canvas; it does not support the full native appearance/annotation/axis contract.