PitchKit
Guides

Layers

The mental model — a pitch container, overlay children, and typed accessors.

Five things to hold in your head — that's the whole model:

  1. <Pitch> owns the coordinate system. Declare the provider via type; every child reads the resulting transform from context.
  2. Children are layers, stacked in render order. Later siblings paint on top of earlier ones, exactly like the DOM.
  3. Accessors map your data to visuals. Every visual prop takes a static value or a typed per-datum function (d, i) => value.
  4. Colours are CSS variables — see Theming.
  5. Responsive is the default — see Responsive.
<Pitch type="statsbomb">
  <Heatmap data={pressures} x={(p) => p.x} y={(p) => p.y} binsX={12} binsY={8} />
  <Arrows data={passes} x={(p) => p.x} y={(p) => p.y} x2={(p) => p.x2} y2={(p) => p.y2} />
  <Scatter data={shots} x={(s) => s.x} y={(s) => s.y} r={(s) => 3 + s.xg * 9} />
</Pitch>

The overlay set

OverlayDrawsGood for
<Scatter>One circle per datumShots, touches, positions
<Arrows>Directional line + head per datumPass maps
<Comet>Tapered, optionally fading trailCarries, runs
<Annotate>Text label per datumPlayer names, callouts
<Heatmap>Binned grid on canvasDensity, xG surfaces
<PositionalHeatmap>Juego de Posición zones on canvasPositional play, zone comparisons
<Hexbin>Hexagonal binned density on canvasDense touch/event maps
<KDE>Smooth density surface on canvasPressure, territory
<Flow>One aggregate arrow per zonePass direction by zone
<Polygon>Arbitrary closed shapeZone highlights
<ConvexHull>Hull of a point setTeam/player shape
<Voronoi>Nearest-player tessellationSpace control
<GoalAngle>Wedge to both postsShot quality context
<PlayerAvatar>Photo, name and events per playerLineups, team sheets

The accessor pattern is what keeps PitchKit shape-agnostic: there's no required data schema — x={(shot) => shot.location[0]} adapts any provider's records without a pre-processing step.

SVG vs canvas

Marks are SVG (crisp, inspectable, hoverable — most take a tooltip accessor). The density layers are the exception: <Heatmap>, <PositionalHeatmap>, <Hexbin> and <KDE> paint to a <canvas>, where a handful of fills beat thousands of DOM nodes. <PlayerAvatar> is HTML laid over the pitch, so its photo can be any image component, next/image included. The split is per-layer and automatic — you compose all three kinds freely in one pitch.

Escape hatch

For custom marks the built-ins don't cover, call usePitch() from any child of <Pitch> to get the pitch dimensions, viewport, and pixel transform, then emit your own SVG. The returned transform.toProvider turns a pixel position back into provider coordinates, which is what you need for hover and click handlers.

On this page