Component

Heatmap

Uniform-grid positional heatmap. Use it when you want discrete bins you can inspect, compare, and label explicitly, including repeated analyst grids where each cell should still keep honest bins. The hero shows Arsenal touch events from a real WhoScored-derived fixture normalized through the adapter layer.

Touches041
Playground

Interactive props.

General-possession touch map. Good baseline for comparing grid and value modes.Pitch theme comes from the floating controller.
Touches041
Stories

Canonical behaviors.

Share Mode

Same pass-origin bins, but the scale and tooltip now report share-of-total instead of raw counts.

Passes0%6%
Fine Grid

Higher-resolution binning is useful for dense event sets, but it becomes noisy more quickly.

Touches016
Positional Zones

Named tactical-zone presets give Heatmap a benchmark-recognizable positional layout without dropping to raw edge arrays.

Touches096
SmallMultiples Cell

Heatmap now has an honest compact-cell seam: hide the scale bar, keep the grid tactical or coarse, and map repeated horizontal cells to attackingDirection='right'.

Final-Third Crop

Attacking-half crop proves that Heatmap still works as a focused inspection surface rather than only a full-pitch chart.

Receptions020
Defensive Shape

Single-hue scales are useful when the story is a defensive action footprint rather than possession territory.

Defensive actions07
Static Export

Heatmap supports the static export pipeline. Hover UI disappears, but the binned surface and scale remain deterministic.

LowHigh
Empty State

No overlay and no fake color cells when nothing is plottable.

No event data
Cross-cutting

Shared concerns.

Choose Heatmap

Use Heatmap when discrete bins matter. Use KDE for a smoothed estimate, and use Territory when the chart should collapse into quick editorial zones.

Interaction

Hover and focus operate at the cell level. valueMode changes tooltip and scale semantics without changing the relative cell ordering.

Themeability

Pitch theming comes from Stadia. Dark sequential ramps automatically force light pitch lines unless autoPitchLines is disabled.

Export

Heatmap is supported by the Phase 1 static export path. Use it when the hover state is not essential and the chart needs a deterministic snapshot.

Composition

Heatmap already owns its scale and tooltip. Wrap it in page layout chrome if needed, but do not rebuild those internals outside the component.

Zone Presets

Use zonePreset when the chart should align to shared tactical layouts such as 18-zone or 20-zone positional bins. Use explicit xEdges and yEdges only when you need a bespoke geometry.

Responsive

Normal, small, smallest.

Pitch-based charts scale fluidly to whatever width the parent gives them. The 'smallest' cell is sized for a 5×4 small-multiples grid (~140px wide). Vertical orientation only — horizontal pitches are too wide for small-multiples.

Touches041
Normal
Touches041
Small
Touches041
Smallest (small multiples)
Usage

Best-practice examples.

Minimal usage

Use Heatmap when you need discrete, inspectable bins rather than a smoothed density estimate.

                    import { Heatmap } from "@withqwerty/campos-react";

type EventPoint = {
  x: number | null;
  y: number | null;
};

type Props = {
  events: EventPoint[];
};

export function TeamHeatmap({ events }: Props) {
  return <Heatmap events={events} metricLabel="Touches" />;
}
                  
Analytical variant

Value mode and grid resolution are the main first-class customizations for a heatmap page.

                    import { Heatmap } from "@withqwerty/campos-react";

type EventPoint = {
  x: number | null;
  y: number | null;
};

type Props = {
  events: EventPoint[];
};

export function TeamHeatmapAnalytical({ events }: Props) {
  return (
    <Heatmap
      events={events}
      metricLabel="Passes"
      colorScale="viridis"
      valueMode="share"
      gridX={25}
      gridY={16}
    />
  );
}
                  
Repeated-grid cell

For SmallMultiples or analyst grids, suppress the scale bar and keep the bins tactical or coarse enough to stay legible.

                    import { Heatmap } from "@withqwerty/campos-react";

type EventPoint = {
  x: number | null;
  y: number | null;
};

type Props = {
  events: EventPoint[];
};

export function TeamHeatmapCell({ events }: Props) {
  return (
    <Heatmap
      events={events}
      zonePreset="5x3"
      attackingDirection="right"
      metricLabel="Pass origins"
      showScaleBar={false}
      framePadding={8}
    />
  );
}
                  
API

Public surface.

Prop Type Default Description
events readonly { x: number | null; y: number | null }[] required Array of positional events in canonical Campos pitch coordinates.
gridX number 12 Number of bins along the attacking axis.
gridY number 8 Number of bins across the pitch width.
zonePreset "3x3" | "5x3" | "18" | "20" Shared named pitch-zone preset. Overrides gridX/gridY unless explicit xEdges/yEdges are supplied.
metricLabel string "Events" Scale-bar and tooltip label for what each event count represents.
valueMode "count" | "intensity" | "share" "count" Controls how tooltip and scale values are expressed. Color cells stay in the same order.
showScaleBar boolean true Toggles the post-plot scale bar. Set false for compact repeated-grid cells.
colorScale "magma" | "viridis" | "inferno" | "blues" | "greens" | "custom" "magma" Sequential color ramp for the cell fills.
colorStops ColorStop[] Custom ramp stops when colorScale='custom'.
attackingDirection "up" | "down" | "left" | "right" "right" Controls which direction of play the pitch projects toward on screen.
crop "full" | "half" "full" Pitch crop. Half filters cells and events to the attacking half.
pitchTheme "primary" | "secondary" "primary" Delegated Stadia surface preset.
pitchColors PitchColors Direct pitch color overrides for branded or editorial surfaces.
autoPitchLines boolean true Automatically switches pitch lines to white on dark ramps so markings stay visible.
cells HeatmapCellsStyle First-class styling surface for rendered heatmap cells.
Use with AI
LLM Prompt
Create a React component using Campos Heatmap. Import Heatmap from @withqwerty/campos-react, keep data parsing outside the component, show the smallest good usage first, then add one analytical variant and one compact repeated-grid cell with showScaleBar={false}.
Pitch