XY Container
Basic Configuration
XY Container is designed as a container that manages multiple XY Components at once, along with optional X and Y axes, tooltip, and crosshair. Here is an example of a common configuration: one XY Component alongside two Axis components:
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisXYContainer data={data}>
<VisStackedBar x={x} y={y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
<vis-xy-container [data]="data">
<vis-stacked-bar [x]="x" [y]="y"></vis-stacked-bar>
<vis-axis type="x"></vis-axis>
<vis-axis type="y"></vis-axis>
</vis-xy-container>
<VisXYContainer {data}>
<VisStackedBar {x} {y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
<VisXYContainer :data="data">
<VisStackedBar :x="x" :y="y" />
<VisAxis type="x" />
<VisAxis type="y" />
</VisXYContainer>
<VisXYContainer data={data}>
<VisStackedBar x={x} y={y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
const container = new XYContainer<DataRecord>(node, {
components: [new StackedBar({ x, y })],
xAxis: new Axis({ type: "x" }),
yAxis: new Axis({ type: "y" })
}, data)
Multiple XY Components
By providing the container with data, every XY Component within the container will be able to use it. There is no limit to the number of XY Components your container can have.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisXYContainer data={data}>
<VisStackedBar x={x} y={y} barWidth={35} color="#72a5ff"/>
<VisLine x={x} y={y}/>
<VisScatter x={x} y={y} shape={shape}/>
</VisXYContainer>
<vis-xy-container [data]="data">
<vis-stacked-bar
[x]="x"
[y]="y"
[barWidth]="35"
color="#72a5ff"
></vis-stacked-bar>
<vis-line [x]="x" [y]="y"></vis-line>
<vis-scatter [x]="x" [y]="y" [shape]="shape"></vis-scatter>
</vis-xy-container>
<VisXYContainer {data}>
<VisStackedBar {x} {y} barWidth={35} color="#72a5ff"/>
<VisLine {x} {y}/>
<VisScatter {x} {y} {shape}/>
</VisXYContainer>
<VisXYContainer :data="data">
<VisStackedBar :x="x" :y="y" :barWidth="35" color="#72a5ff" />
<VisLine :x="x" :y="y" />
<VisScatter :x="x" :y="y" :shape="shape" />
</VisXYContainer>
<VisXYContainer data={data}>
<VisStackedBar x={x} y={y} barWidth={35} color="#72a5ff"/>
<VisLine x={x} y={y}/>
<VisScatter x={x} y={y} shape={shape}/>
</VisXYContainer>
const container = new XYContainer<DataRecord>(node, {
components: [
new StackedBar({ x, y, barWidth: 35, color: "#72a5ff" })
new Line({ x, y })
new Scatter({ x, y, shape })
]
}, data)
Chart Sizing
Width and Height
By default, XY Container will try to fit within the bounds of its parent HTML element. If the parent
height isn't defined, the default height of 300px will be applied. You can also explicitly define the container's
size with the width and height properties, which accepts numeric values.
const width = 200;
const height = 100;
Margin
You can set XY Container's margin to control the spacing between the container and adjacent
elements. The margin property accepts a value of type Sizing, where each value represents the
corresponding margin size in pixels.
Sizing
type Sizing = {
top: number;
bottom: number;
left: number;
right: number;
}
Note that the chart's size is affected by this property. Notice how the following chart is affected after setting the margin accordingly.
const margin = { left: 100, right: 100 }
Before:
After:
Padding
You can configure the padding property of XY Container to change how its children
are spaced apart. This property also accepts value of type Sizing.
In contrast to margin, the padding property will not affect the overall size of the chart, but
rather tha size of the individual components. See how this works using the same example as above:
const padding = { left: 100, right: 100 }
Bleed
Some components need extra space inside the container for their marks to fully fit: half of the point diameter for Scatter, half of the bar width for bar charts at the edges of the domain, and so on. This space is called bleed. Before rendering, the container asks every component how much bleed it needs, combines the values (taking the maximum for each side), and shrinks the scale ranges accordingly. That's why the first and the last points of the scatter chart below are shifted inward from the container's edges instead of being cut in half:
The container reports the bleed it used to the onRenderComplete callback, and you can override the
calculated values with the bleed property. It accepts a Spacing object, or a
function that receives the container's components and returns one. The sides you provide replace the
calculated values, the sides left undefined fall back to them. Here is the same chart with the
bleed set to zero on every side — the edge points now get clipped:
const bleed = { top: 0, bottom: 0, left: 0, right: 0 }
Overriding the bleed is mainly useful for aligning the X values of several charts placed one below another — see the Bleed guide for a complete walkthrough.
Range
The xRange and yRange determine the screen space your chart contains. By default, an XY Container will
fit to its container. Provide xRange with values [xStart, width] and yRange with [yStart, height]
to override the default configuration.
Automatic Margins
By default autoMargin is true: the container measures the X and Y axes and reserves the margin needed so
their tick labels and titles aren't clipped. Set it to false when you'd rather control the spacing yourself
through the margin property — for example, to line several charts up on a shared left edge:
<VisXYContainer data={data} autoMargin={false} margin={{ left: 60, bottom: 30 }}>
{/* … */}
</VisXYContainer>
Domain
You can change the domain of your data with the xDomain and yDomain properties to configure which values your XY Component should display.
The result will show all data that is in this range- excluding any values that fall outside of the range, and occupying any missing values with white space.
See the following example, which looks like this when the domain is not set:
xDomain = [4,8]
yDomain = [0,100]
Domain Constraints
Customizing your domain is helpful in datasets with outliers. When using dynamic data, you may not know which values to constrain your domain values to.
With the following constraint properties xDomainMinConstraint, xDomainMaxConstraint, yDomainMinConstraint, and yDomainMaxConstraint, you can define partial domains.
For the following examples, consider the following case where the majority of the data is within the ranges [0,10] for all values of x and y.
These properties accept a number array in the form [number, number] or more typically [number, undefined] | [undefined, number].
Domain Constraints: None
xDomainMinConstraint: [0, undefined]
yDomainMaxConstraint: [undefined, 10]
Single Data Point
When the calculated domain is empty (when you have only a single data element and the domain's min and max values are equal), use the preventEmptyDomain
property to extend it by +1. This may be useful when you have no data or a single data point and you
want to show the empty space. The possible values are:
true: automatically extend the domain by+1when the domain is empty (domain start equals domain end);null: extend the domain, but only when there's no data (default);false: keep the domain as is.
For example, a grouped bar with a single data point at x=1:
Scale
To change the scale of one of your axes in your Container, use the xScale or yScale property and a Scale function (i.e. Scale.scaleLinear()).
Currently, only continuous scales are supported. See d3-scale
for more information about the meaning behind these functions.
import { Scale } from '@unovis/ts'
const yScale = Scale.scaleLinear()
const yScale = Scale.scalePow().exponent(2)
Dynamic Y Scale
You can also set the yScale domain dynamically based on the current xDomain, meaning that only visible data will be used in the domain calculation.
Take a look at the example below. The xDomain configuration property there is not set, the chart displays the whole dataset and the Y axis shows ticks from 0 to 150.
Let's set xDomain to [0, 50]. The chart now shows only a portion of the original data but the Y axis still displays the whole data range.
Setting scaleByDomain to true will tell the chart to dynamically calculate the domain of yScale based on the data within xDomain.
It comes in hand when you're updating xDomain programmatically using, for example, the Brush component to provide some navigation capabilities to a chart with lots of data points.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisXYContainer
data={data}
xDomain={[0,50]}
scaleByDomain={true}
>
<VisStackedBar x={x} y={y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
<vis-xy-container
[data]="data"
[xDomain]="[0,50]"
[scaleByDomain]="true"
>
<vis-stacked-bar [x]="x" [y]="y"></vis-stacked-bar>
<vis-axis type="x"></vis-axis>
<vis-axis type="y"></vis-axis>
</vis-xy-container>
<VisXYContainer
{data}
xDomain={[0,50]}
scaleByDomain={true}
>
<VisStackedBar {x} {y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
<VisXYContainer
:data="data"
:xDomain="[0,50]"
:scaleByDomain="true"
>
<VisStackedBar :x="x" :y="y" />
<VisAxis type="x" />
<VisAxis type="y" />
</VisXYContainer>
<VisXYContainer
data={data}
xDomain={[0,50]}
scaleByDomain={true}
>
<VisStackedBar x={x} y={y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
const container = new XYContainer<DataRecord>(node, {
xDomain: [0,50],
scaleByDomain: true,
components: [new StackedBar({ x, y })],
xAxis: new Axis({ type: "x" }),
yAxis: new Axis({ type: "y" })
}, data)
Y Direction
You can set yDirection to change the direction of data along the Y axis. Supported values are Direction.North and Direction.South.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisXYContainer data={data} yDirection="south">
<VisStackedBar x={x} y={y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
<vis-xy-container [data]="data" yDirection="south">
<vis-stacked-bar [x]="x" [y]="y"></vis-stacked-bar>
<vis-axis type="x"></vis-axis>
<vis-axis type="y"></vis-axis>
</vis-xy-container>
<VisXYContainer {data} yDirection="south">
<VisStackedBar {x} {y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
<VisXYContainer :data="data" yDirection="south">
<VisStackedBar :x="x" :y="y" />
<VisAxis type="x" />
<VisAxis type="y" />
</VisXYContainer>
<VisXYContainer data={data} yDirection="south">
<VisStackedBar x={x} y={y}/>
<VisAxis type="x"/>
<VisAxis type="y"/>
</VisXYContainer>
const container = new XYContainer<DataRecord>(node, {
yDirection: "south",
components: [new StackedBar({ x, y })],
xAxis: new Axis({ type: "x" }),
yAxis: new Axis({ type: "y" })
}, data)
Color
Use the colorFunction property to give every component inside the container a single color function of type
(key: string | number) => string. Each component resolves its color through this function, passing either a
data color key (from the component's colorKeys property) or, as a fallback, the series index. Supplying
one colorFunction at the container level — and reusing it across other containers and the BulletLegend —
is how you keep a category's color consistent across multiple charts.
import { Scale } from '@unovis/ts'
const color = Scale.scaleOrdinal().range(['#4d8cfd', '#ff6b7e', '#00c19a'])
In the example below the StackedBar's three series are labeled with colorKeys, and the container's
colorFunction maps each key to a color:
For the full walkthrough — color keys, building a color function, syncing the legend and tooltip, and overriding the default palette — see Synchronizing Colors Across Charts in the Theming guide.
Animation
Set duration (in milliseconds) on the container to control how long transitions take when the data or
configuration updates. It applies to every component inside the container. The top chart below animates over
1500ms while the bottom one uses duration={0} for instant updates — press the toggle to alternate the data:
Tooltip
Attach a Tooltip to show details on hover. Provide a triggers map that pairs a
component's selector with a template function returning an HTML string:
See the Tooltip docs for positioning, show/hide delays, and custom rendering.
Crosshair
A Crosshair renders a vertical line that follows the pointer and marks the
nearest value on every series — ideal for line and area charts. Provide a template to show a tooltip
alongside it:
See the Crosshair docs for custom accessors, show/hide behavior, and styling.
Annotations
Use Annotations to overlay text labels and callouts. Pass an items array;
each item is positioned in data coordinates or with '%' strings and can point at a subject:
See the Annotations docs for the full set of AnnotationItem options.
Accessibility
Pass a descriptive ariaLabel to set an aria-label on the chart's container div, giving screen-reader
users a text summary of the visualization:
<VisXYContainer data={data} ariaLabel="Monthly revenue by region, in US dollars">
{/* … */}
</VisXYContainer>
SVG Defs
You can use the svgDefs property to inject custom SVG definitions — gradients, patterns, clip paths, and
filters — that your components can reference by id. See our
Theming guide, or the
Gradient Fills and
SVG Filters sections of the Theming guide, for details.
For ready-made stripe, dot, and hatch fills you don't need custom defs — use a component's pattern accessor
or the theme-patterns class. See Patterns in the Theming guide.
Component Props
| Name | Type | Description |
|---|---|---|
| * required property |