@visx/group
@visx/group exports one React component, Group, that renders an SVG <g> container. It converts numeric left and top props into a translate(left, top) transform, always adds the visx-group class, accepts an optional full transform string, forwards ordinary SVG group attributes, and exposes the underlying SVGGElement through innerRef. It is a convenience layer for positioning chart regions, axes, marks, and labels, not a charting system or layout engine.
A clean, tiny convenience for teams already building charts from visx primitives. Do not install it as a standalone chart solution, and consider a native <g> when translation sugar is the only feature you need.
Use it if
- You already compose charts from visx packages and want the same left and top positioning convention across components
- You repeatedly write SVG groups with translate transforms for chart margins, series, axes, or small multiples
- You want typed React SVG group props plus an explicit innerRef to the rendered SVGGElement
- You prefer a low-level primitive that leaves scales, marks, animation, accessibility, and interaction under your control
- You only need one or two groups: native <g transform={`translate(${x}, ${y})`}> is clearer and avoids a package for a 24-line component
- Your app is on React 16 or 17: @visx/group 4.0.0 declares React 18 or 19 as its peer range, and the migration guide says older consumers should stay on visx 3
- You want finished charts with legends, tooltips, responsive containers, sensible defaults, and animation: Group provides none of those and the visx README says animation is intentionally not baked in
- You need HTML or Canvas grouping: the component always renders an SVG <g>, which only has useful rendering semantics inside an SVG tree
- You expect top and left to combine with a custom transform: any truthy transform prop replaces the generated translation, so rotation or scaling must include its own translate operation
Setup reality
Install with npm install @visx/group. Version 4.0.0 has one runtime dependency, classnames, and peer dependencies on React 18 or 19 plus optional matching @types/react. A React 16 or 17 project should remain on visx 3 rather than forcing the peer range. The package publishes CommonJS and ESM entry points, an exports map, and bundled TypeScript declarations, so ordinary modern bundlers need no config. The component does not create an <svg>; Group always emits <g>, and using it outside an SVG produces no visible chart. Its default transform is translate(left, top), with missing offsets treated as zero. Passing any truthy transform string replaces both offsets instead of composing with them. An empty transform string is also not a way to remove the attribute because the implementation falls back to translate(0, 0). The component always prepends the visx-group class through classnames. Other SVG attributes and event handlers are forwarded, but Group does not calculate margins, bounds, scales, clipping, responsiveness, hit areas, focus behavior, or ARIA labels. The ref prop documented by this package is innerRef, and it points at the actual SVGGElement after mount. Server rendering is straightforward because output is plain SVG markup, but measurements from getBBox still require a browser layout pass in an effect. Installing only @visx/group does not install @visx/shape, @visx/scale, axes, tooltips, or mock data; add those packages separately and keep all visx packages on compatible major versions.
Patterns
Offset SVG children with left and toptranslate-group
import { Group } from '@visx/group'
<svg width={320} height={180}>
<Group left={24} top={16}>
<circle cx={20} cy={20} r={12} fill="tomato" />
</Group>
</svg>Group renders transform="translate(24, 16)". It does not change the SVG viewport or reserve layout space.
Create an inner chart area from marginsapply-chart-margins
const margin = { top: 20, right: 16, bottom: 32, left: 48 }
const innerWidth = width - margin.left - margin.right
const innerHeight = height - margin.top - margin.bottom
<svg width={width} height={height}>
<Group left={margin.left} top={margin.top}>
<rect width={innerWidth} height={innerHeight} fill="none" />
</Group>
</svg>Group only applies the offset. Your code remains responsible for subtracting margins from scale ranges and mark dimensions.
Nest groups for local coordinate systemsnest-coordinate-systems
<Group left={margin.left} top={margin.top}>
<Group left={plotX} top={plotY}>
{points.map((point) => (
<circle key={point.id} cx={point.x} cy={point.y} r={3} />
))}
</Group>
</Group>SVG transforms compose through nesting. The inner point coordinates are relative to both translations.
Supply a complete SVG transformset-custom-transform
<Group transform="translate(120 80) rotate(-30) scale(1.2)">
<rect x={-20} y={-8} width={40} height={16} />
</Group>A truthy transform overrides top and left completely. Put translate in the transform string when rotation or scale must follow an offset.
Group each data seriesrender-series-groups
{series.map((item, seriesIndex) => (
<Group
key={item.id}
className="chart-series"
top={seriesIndex * rowHeight}
data-series-id={item.id}
>
<path d={item.path} fill={item.color} />
</Group>
))}Use stable data keys. Group forwards data attributes and combines your class with its built-in visx-group class.
Apply inherited SVG presentationstyle-group
<Group className="muted-series" fill="#64748b" stroke="currentColor" opacity={0.7}>
<circle cx={10} cy={10} r={4} />
<circle cx={30} cy={20} r={4} />
</Group>Many SVG presentation attributes inherit to children, but a child's explicit fill, stroke, or opacity can override the group.
Attach React events to a grouphandle-pointer-events
<Group
role="button"
tabIndex={0}
onClick={() => selectSeries(series.id)}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') selectSeries(series.id)
}}
>
{marks}
</Group>Group forwards handlers but supplies no keyboard semantics. Add role, focusability, key handling, and an accessible name when the group is interactive.
Give a graphic group an accessible labellabel-accessibly
<Group role="img" aria-labelledby="revenue-title revenue-desc">
<title id="revenue-title">Monthly revenue</title>
<desc id="revenue-desc">Bars from January through June</desc>
{bars}
</Group>The component adds no accessibility metadata. Test SVG title and description behavior with the browsers and assistive technologies your application supports.
Apply an SVG clip path to grouped marksclip-chart-marks
<svg width={width} height={height}>
<defs>
<clipPath id="plot-clip">
<rect width={innerWidth} height={innerHeight} />
</clipPath>
</defs>
<Group left={margin.left} top={margin.top} clipPath="url(#plot-clip)">
{marks}
</Group>
</svg>Use a unique clipPath id per rendered chart, especially with server rendering or repeated dashboard widgets, to avoid document-wide ID collisions.
Measure the rendered SVG groupmeasure-group
import { useLayoutEffect, useRef } from 'react'
import { Group } from '@visx/group'
const groupRef = useRef<SVGGElement>(null)
useLayoutEffect(() => {
const bounds = groupRef.current?.getBBox()
if (bounds) console.log(bounds)
}, [])
return <Group innerRef={groupRef}>{marks}</Group>getBBox requires browser SVG layout and is unavailable during server rendering. Measure after mount and account for later font or data changes.
Place a bottom axis in chart coordinatesposition-axis
import { AxisBottom } from '@visx/axis'
<Group left={margin.left} top={margin.top}>
{marks}
<Group top={innerHeight}>
<AxisBottom scale={xScale} />
</Group>
</Group>@visx/axis is a separate installation. Keep its major version compatible with @visx/group and pass the same scale used for marks.
Replace the helper with native SVG when appropriateuse-native-group
const transform = `translate(${left}, ${top})`
<g className="series" transform={transform}>
{children}
</g>This native form covers the main behavior without a dependency. Prefer it when your project does not otherwise use visx conventions.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| recharts | npm | Use when you want composed React charts with axes, tooltips, legends, responsiveness, and animation supplied as a higher-level system |
| victory | npm | Use when a declarative React chart suite is a better fit than assembling low-level SVG primitives yourself |
| d3-selection | npm | Use when React is not driving SVG ownership and you want D3's selection, data join, and direct DOM grouping model |