@visx/grid
@visx/grid is the SVG gridline package in Airbnb's visx collection of low-level React visualization primitives. GridRows draws horizontal lines from a y scale, GridColumns draws vertical lines from an x scale, and Grid combines both. GridAngle, GridRadial, and GridPolar cover circular charts. You provide scales, pixel dimensions, tick policy, margins, and styling; the components turn scale ticks into React SVG groups, lines, and arcs. It is chart infrastructure, not a complete chart with axes, data marks, tooltips, responsiveness, animation, or accessibility semantics.
Excellent grid primitives for teams already choosing visx because they want to own chart composition. If you are not excited to manage scales, sizing, layering, and accessibility yourself, install a higher-level chart library instead.
Use it if
- You are building a custom SVG chart in React and want grid geometry to stay aligned with the same D3 or visx scales as your axes and marks
- You need independent row and column styling, exact tick values, custom line rendering, or polar grid primitives
- You already use other visx v4 packages and prefer composable pieces over a chart configuration framework
- You need TypeScript-friendly components that support both React 18 and React 19 and publish ESM plus CommonJS entry points
- You want a chart from data in one component: @visx/grid supplies only gridlines, so you still need scales, axes, marks, margins, labels, tooltips, legends, and responsive measurement
- Your application is on React 16 or 17 or must support IE11: the v4 migration guide requires React 18 or 19 and says legacy-browser users should remain on visx 3
- You need Canvas or WebGL for a very dense interactive plot: these components emit one SVG line or path per tick and do not provide a non-SVG renderer
- You need built-in animation or transitions: the visx README says animation is intentionally not included and expects consumers to choose a React animation library
- You only need two static background lines: version 4.0.0 pulls @visx/curve, group, point, scale, shape, and classnames, which is more dependency surface than a few hand-written SVG elements
Setup reality
Install `@visx/grid` alongside React 18 or 19. React is a peer dependency, and TypeScript projects should install the matching `@types/react`; that types peer is optional for JavaScript consumers. The package itself depends on six runtime packages: five visx modules plus classnames. Version 4 publishes an exports map with ESM, CommonJS, and declarations, so use package-root imports such as `import { Grid } from '@visx/grid'`; deep imports used by older examples are blocked and unsupported. Upgrade every @visx package in an application to v4 together because the migration guide says their entry points and internal versions changed as a set. The first surprise is how much chart plumbing remains. Grid requires xScale, yScale, width, and height, and those dimensions must already exclude margins if the grid sits inside a translated plot group. It does not observe its container; add @visx/responsive or your own ResizeObserver. `numTicks` is only a hint because D3 chooses pleasant ticks, while `tickValues` provides exact control. Band scales are centered automatically by adding half their bandwidth, and the offset prop adds to that center rather than replacing it. SVG paint order matters, so render grids before marks if they belong behind the data. Lines can intercept pointer events unless you disable pointer events, and decorative grids should be hidden from assistive technology with `aria-hidden`. Grid has no theme context, axis synchronization, clipping, or animation. Polar components use radians, require compatible angle and radial scales, and may need explicit tick arrays to avoid unsuitable domain-derived circles. Server rendering is straightforward because the components are pure React, but responsive measurement happens only in the browser and must avoid a zero-size first layout. Finally, v4 removed generated runtime PropTypes, so JavaScript applications get no runtime prop validation; bad scales or undefined dimensions can quietly produce lines at zero coordinates.
Patterns
Draw rows and columns from two scalesdraw-cartesian-grid
import { Grid } from '@visx/grid';
import { scaleLinear } from '@visx/scale';
const width = 640;
const height = 320;
const xScale = scaleLinear({ domain: [0, 100], range: [0, width] });
const yScale = scaleLinear({ domain: [0, 1], range: [height, 0] });
const grid = (
<svg width={width} height={height}>
<Grid
xScale={xScale}
yScale={yScale}
width={width}
height={height}
numTicksRows={5}
numTicksColumns={10}
/>
</svg>
);Tick counts are approximate. The scale range and grid dimensions must describe the same plot area.
Render only horizontal grid rowsdraw-horizontal-rows
import { GridRows } from '@visx/grid';
<GridRows
scale={yScale}
width={plotWidth}
numTicks={6}
stroke='#d7dde5'
strokeWidth={1}
/>;GridRows expects a y-position scale. It draws each line from x=0 through the supplied width.
Center columns on a band scaledraw-band-columns
import { GridColumns } from '@visx/grid';
import { scaleBand } from '@visx/scale';
const xScale = scaleBand({
domain: ['Mon', 'Tue', 'Wed'],
range: [0, plotWidth],
padding: 0.2,
});
<GridColumns scale={xScale} height={plotHeight} />;GridColumns adds half of scale.bandwidth() automatically, so each line lands at the center of its band.
Choose exact gridline valuesset-exact-ticks
import { Grid } from '@visx/grid';
<Grid
xScale={xScale}
yScale={yScale}
width={plotWidth}
height={plotHeight}
columnTickValues={[0, 25, 50, 75, 100]}
rowTickValues={[0, 0.5, 1]}
/>;Explicit tick arrays override the approximate numTicks props and must contain values accepted by their corresponding scales.
Style rows and columns independentlystyle-dashed-grid
import { Grid } from '@visx/grid';
<Grid
xScale={xScale}
yScale={yScale}
width={plotWidth}
height={plotHeight}
stroke='#94a3b8'
strokeDasharray='3 4'
rowLineStyle={{ opacity: 0.55 }}
columnLineStyle={{ opacity: 0.25 }}
/>;The shared stroke props apply to both directions; rowLineStyle and columnLineStyle handle direction-specific CSS properties.
Align the grid with chart marginsposition-with-margins
import { Group } from '@visx/group';
import { Grid } from '@visx/grid';
const margin = { top: 20, right: 24, bottom: 40, left: 48 };
const plotWidth = width - margin.left - margin.right;
const plotHeight = height - margin.top - margin.bottom;
<svg width={width} height={height}>
<Group left={margin.left} top={margin.top}>
<Grid xScale={xScale} yScale={yScale} width={plotWidth} height={plotHeight} />
{dataMarks}
</Group>
</svg>;Configure the scales' ranges with plotWidth and plotHeight too. Applying the margin twice shifts grid and marks apart.
Keep decorative lines behind the datalayer-behind-marks
<svg width={width} height={height}>
<Grid
xScale={xScale}
yScale={yScale}
width={width}
height={height}
aria-hidden='true'
pointerEvents='none'
/>
<DataSeries data={data} />
</svg>;SVG uses document order for paint order. Render the grid first, and hide decorative lines from pointer hit-testing and assistive technology.
Render custom horizontal linesrender-custom-lines
import { GridRows } from '@visx/grid';
<GridRows scale={yScale} width={plotWidth} tickValues={[0, 25, 50, 75, 100]}>
{({ lines }) => (
<g>
{lines.map(({ from, to, index }) => (
<line
key={index}
x1={from.x}
y1={from.y}
x2={to.x}
y2={to.y}
stroke={index === 0 ? '#334155' : '#cbd5e1'}
strokeWidth={index === 0 ? 2 : 1}
/>
))}
</g>
)}
</GridRows>;The render function replaces the default Line elements. Keys use the generated index, so keep tick ordering stable between renders.
Measure a container with ParentSizemake-grid-responsive
import { ParentSize } from '@visx/responsive';
import { Grid } from '@visx/grid';
<ParentSize debounceTime={100}>
{({ width, height }) => {
if (width < 1 || height < 1) return null;
const x = makeXScale(width);
const y = makeYScale(height);
return (
<svg width={width} height={height}>
<Grid xScale={x} yScale={y} width={width} height={height} />
</svg>
);
}}
</ParentSize>;Responsiveness is a separate @visx/responsive concern. The first browser measurement can be zero, and server output has no measured size.
Draw spokes for a radial chartdraw-angle-spokes
import { GridAngle } from '@visx/grid';
import { scaleLinear } from '@visx/scale';
const angleScale = scaleLinear({ domain: [0, 8], range: [0, Math.PI * 2] });
<GridAngle
scale={angleScale}
tickValues={[0, 1, 2, 3, 4, 5, 6, 7]}
innerRadius={24}
outerRadius={140}
left={160}
top={160}
/>;Angle scale output is in radians. GridAngle rotates its Cartesian conversion so zero begins at the top.
Draw concentric radial grid ringsdraw-radial-rings
import { GridRadial } from '@visx/grid';
import { scaleLinear } from '@visx/scale';
const radiusScale = scaleLinear({ domain: [0, 100], range: [0, 140] });
<GridRadial
scale={radiusScale}
tickValues={[20, 40, 60, 80, 100]}
left={160}
top={160}
stroke='#cbd5e1'
fill='transparent'
/>;GridRadial derives the inner radius from the minimum scale domain value. Use explicit ticks when the domain would create an unwanted center ring.
Combine spokes and ringsdraw-polar-grid
import { GridPolar } from '@visx/grid';
<GridPolar
scaleAngle={angleScale}
scaleRadial={radiusScale}
outerRadius={140}
left={160}
top={160}
tickValuesAngle={[0, 1, 2, 3, 4, 5, 6, 7]}
tickValuesRadial={[20, 40, 60, 80, 100]}
strokeAngle='#e2e8f0'
strokeRadial='#cbd5e1'
strokeDasharrayRadial='2 3'
/>;GridPolar composes GridAngle and GridRadial. Its angle and radial scales need compatible domains and ranges; the component does not infer chart geometry from data.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| recharts | npm | You want responsive React charts with CartesianGrid, axes, tooltips, legends, and common series assembled at a higher level |
| victory | npm | You want a batteries-included React chart system with declarative axes, grids, themes, and animation |
| @nivo/line | npm | You want an opinionated responsive line chart with grids, legends, interactions, SVG or Canvas variants, and polished defaults |