TanStack Charts treats width and height differently:
This keeps a definition stable while the same chart moves between a dashboard card, a split pane, and a full-width report.
Omit width from mountChart or framework adapter options to follow the container. The shared DOM host observes the container and updates only when its measured width changes.
const host = mountChart(element, {
definition,
height: 320,
ariaLabel: 'Weekly downloads',
})The container must have a resolvable width. In grid and flex layouts, the common requirement is min-width: 0 on the grid or flex child:
.chart-card {
min-width: 0;
}Supply width only when an application deliberately owns fixed geometry, such as an export frame or a benchmark.
Use one of these policies:
Do not supply both as competing policies. A fixed height is usually more stable for dashboards and scrolling pages. An aspect ratio is useful for editorial layouts where the chart should scale as one visual block. Invalid ratios fall back to the default height.
Omit margin for normal charts. The scene solver reserves the minimum space required by:
import { barX, defineChart } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { rows } from './data'
export default defineChart({
marks: [
barX(rows, {
x: 'weeklyRequests',
y: 'feature',
fill: '#7c3aed',
inset: 1,
}),
],
x: {
scale: scaleLinear,
nice: true,
grid: true,
axis: { ticks: { count: 5 }, label: 'Weekly requests' },
},
y: {
scale: () => scaleBand<string>().padding(0.1),
},
})export const rows = [
{ feature: 'Self-service account recovery', weeklyRequests: 128 },
{ feature: 'Saved searches and alerting', weeklyRequests: 95 },
{ feature: 'Audit log export', weeklyRequests: 72 },
{ feature: 'Custom retention policies', weeklyRequests: 44 },
{ feature: 'Role-based access controls', weeklyRequests: 31 },
]An explicit side locks only that side:
margin: {
left: 96
}margin: 0 locks every side and is appropriate for guide-free sparklines. Axis labels thin automatically after candidate generation and optional rotation. Use axis.ticks.spacing, axis.tickLabels.rotate, hard-kept labels, or a different representation when labels compete for the same axis space.
Open the full horizontal-ranking catalog case.
Static scenes use deterministic text estimates. DOM and framework SVG hosts measure painted text with the inherited container font and relayout after web fonts finish loading.
Use measureText only when another renderer or layout system owns text measurement. It runs synchronously during scene compilation and receives the text plus the resolved font family, style, stretch, size, weight, letter spacing, direction, locale, font scale, anchor, and baseline. It returns the painted box relative to the requested origin. A host that loads fonts or measures them asynchronously owns that readiness lifecycle and renders the scene again when its synchronous metrics change.
The resolved geometry is available after every render:
const scene = host.getScene()
scene.margin
scene.chart
scene.scalesUse scene.chart to align application-owned overlays. Do not calculate a parallel plot rectangle from guessed margins.
A server cannot measure a future browser container. Supply initialWidth when the initial SVG must have deterministic geometry:
<Chart
definition={definition}
initialWidth={640}
height={320}
ariaLabel="Weekly downloads"
/>The client adopts the real container width after hydration. Use the same initialWidth for all requests that render the same layout; do not derive it from browser-only APIs on the server.
See SSR and Hydration for the complete lifecycle.
Most data transforms should depend only on application data. The responsive builder receives the full scene width and height, so it can choose breakpoints, tick counts, or a surface-relative representation.
Those values are not the final inner plot bounds. Automatic guides and legends resolve scene.chart after the builder returns. Exact plot-space collision, binning, or label placement belongs in a custom mark's render phase, which receives the final chart bounds and resolved scales, or in an application overlay driven by onRender.
function summarizeRows(rows: Input['rows']) {
return summarize(rows)
}
function createDefinition(rows: Input['rows']) {
const summary = summarizeRows(rows)
return defineChart(({ width }) =>
buildResponsiveSpec(summary, {
compact: width < 480,
}),
)
}For a deliberately guide-free margin: 0 scene, the full surface and inner plot can coincide. Do not assume that equivalence for ordinary automatic layout.
Responsive relayout commits immediately even when animation is enabled for data updates. Set svgAnimation: { resize: true } only when size interpolation is intentional.
The Dynamic Data and Animation guide explains the two phases. The D3 integration contract explains why application code should not set positional pixel ranges.