TanStack
Mark Reference

Polar Marks

Polar marks are available only from the capability subpath:

ts
import {
  angleGrid,
  pie,
  polar,
  radialArc,
  radialArea,
  radialBarAngle,
  radialBarRadius,
  radialDot,
  radialGrid,
  radialLine,
  radialRule,
  radialText,
} from '@tanstack/charts/polar'

polar resolves the responsive coordinate system. The exported PolarMark and PolarGuide types are opaque composition contracts returned by the built-in radial mark and guide constructors. Use them to type collections passed to polar; do not implement their internal initialize or render lifecycle. Guide backgrounds paint first, marks paint second, and guide foregrounds paint last.

polar

ts
function polar(options: PolarOptions): ChartMark
OptionTypeDefaultMeaning
idstringLayer-derivedStable container ID
classNamestringNoneClass added beside ts-chart__polar
marksreadonly PolarMark[]RequiredRadial marks rendered in order
guidesreadonly PolarGuide[][]Background/foreground guide layers around marks
anglePolarAngleOptionsNoneAngle factory or instance and optional wrapping
radiusPolarRadiusOptionsNoneRadius scale and optional responsive pixel range
startAnglenumber0Start of the available angular range in radians
endAnglenumberEnd of the available angular range in radians
insetnumber0Pixels removed from the maximum centered radius
radiusRationumber1Multiplier applied to the radius after inset

The default angular range is a complete circle. Angles use D3's radial convention: zero is at twelve o'clock and positive values move clockwise.

PolarAngleOptions and PolarRadiusOptions accept compatible factories with mark-inferred domains or configured instances with fixed domains. nice applies after inference. TanStack supplies responsive ranges without mutating an instance. An omitted wrap closes a complete revolution without adding a duplicate semantic category, but preserves both endpoints of a partial range. Set it explicitly to override that behavior.

PolarRadiusOptions.range overrides the default [0, radius] pixel range on the copied radius scale. Each endpoint is a nonnegative pixel length or a PolarLength callback, so concentric layouts can resolve physical ranges from the final radius:

ts
const radiusOptions = {
  scale: scaleLinear().domain([0, maximum]),
  range: [({ radius }) => radius * 0.2, ({ radius }) => radius],
}

The range is re-resolved on resize and never mutates the authored D3 scale.

PolarLayoutContext contains chart, centerX, centerY, radius, startAngle, endAngle, and optional resolved angle/radius scales. Each PolarResolvedScale exposes its semantic domain, responsive map, ticks, and bandwidth. A PolarLength is either a pixel length or a callback of the layout context. Use a callback for radii that must remain proportional during resize.

The outer chart omits x and y. Cartesian axes do not participate in the internal polar scales.

pie

ts
function pie<TDatum extends object>(
  source: Iterable<TDatum>,
  options: PieOptions<TDatum>,
): PieDatum<TDatum>[]

pie eagerly allocates a nonnegative value channel into angle intervals. It does not render geometry or depend on chart dimensions.

OptionTypeDefaultMeaning
valueTransformValue<number>RequiredNonnegative value allocated to each interval
orderByTransformValueNoneExplicit angular ordering value
orderascending | descendingascendingDirection applied to orderBy
startAnglenumber0Overall start angle in radians
endAnglenumberOverall end angle in radians
gapAnglenumber0Direct empty angle between visible slices

Output rows remain in source order. index records angular order, value is the resolved finite value, fraction is its share of the positive total, and startAngle, endAngle, and angle are the visible interval and midpoint. Each row also carries direct source and sourceIndexes lineage. Missing or non-finite values are omitted, zero is retained, and negative values fail.

gapAngle is radius-independent. A complete revolution includes a seam gap; a partial range uses only internal gaps and preserves both authored endpoints. The output padAngle is intentionally 0 compatibility metadata so the default radialArc accessors do not pad an already-gapped interval a second time. Use the mark's padAngle and padRadius only when D3's radius-dependent arc padding is wanted instead.

The derived fields value, index, fraction, startAngle, endAngle, angle, padAngle, source, and sourceIndexes overwrite source fields with the same names. Stable identity is not synthesized; preserve a semantic source field and pass it to the consuming mark's key channel.

radialArc

ts
function radialArc<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialArcOptions<TDatum>,
): PolarMark<TDatum>

radialArc renders one D3 arc per valid interval.

OptionMeaning
id, classNameStable layer ID and optional class
startAngleStart-angle channel; defaults to datum startAngle
endAngleEnd-angle channel; defaults to datum endAngle
padAnglePadding-angle channel; defaults to datum padAngle, then zero
innerRadiusPolarLength; defaults to zero
outerRadiusPolarLength; defaults to the layout radius
cornerRadiusD3 arc corner radius as a PolarLength
padRadiusExplicit D3 arc padding radius as a PolarLength
generatorResponsive D3 arc factory for advanced per-datum geometry
keyStable arc identity; defaults to top/nested id, then index
zGeometry and interaction group
colorColor-scale value; defaults to z
fillFinal constant or datum-derived paint override
fillOpacityFill opacity
strokeConstant or datum-derived boundary stroke
strokeOpacityBoundary opacity
strokeWidthBoundary width
strokeDasharrayBoundary dash array
opacityWhole-arc opacity

Use the native pie transform for flat typed rows with source lineage. D3 pie output remains valid interoperability input because its startAngle, endAngle, and padAngle fields are also the channels this mark needs. A pie, donut, and gauge differ only in inner radius and angular interval.

generator replaces the default D3 arc configuration for bespoke per-datum geometry. Its factory receives the final PolarLayoutContext; keep the D3 generator context null so it returns SVG path data. Standard hierarchy partitioning belongs to the optional sunburst mark, which accepts flat source rows and preserves their lineage.

radialBarRadius and radialBarAngle

ts
function radialBarRadius<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialBarRadiusOptions<TDatum>,
): PolarMark<TDatum>

function radialBarAngle<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialBarAngleOptions<TDatum>,
): PolarMark<TDatum>

The two radial-bar marks transpose ordinary bar semantics across polar axes. radialBarRadius uses an angle band and a quantitative radius interval; radialBarAngle uses a radius band and a quantitative angle interval. The categorical scale must have positive bandwidth. Configure spacing through the D3 band scale's inner and outer padding.

MarkCategorical channelQuantitative interval
radialBarRadiusangle; defaults to row indexradius is shorthand for radius2; radius1 is the optional baseline
radialBarAngleradius; defaults to row indexangle is shorthand for angle2; angle1 is the optional baseline

An omitted radialBarRadius.radius1 starts at physical radius zero, even when PolarRadiusOptions.range maps semantic zero to an inner offset. An explicit radius1 is mapped through the radius scale. An omitted radialBarAngle.angle1 is semantic zero and is mapped through the angle scale. Use the implicit physical-center radius baseline for nonnegative magnitudes. For signed values or true radial intervals, set radius1: 0 (or another semantic baseline) so both endpoints map through the configured scale.

Both marks accept id, className, key, z, color, fill, fill opacity, stroke styling, opacity, and motion. cornerRadius accepts a PolarLength or "full"; the latter resolves to half the bar's radial thickness. Each valid bar emits one geometry-backed interaction point at its quantitative endpoint and preserves its interval endpoints for focus and tooltip formatting.

radialLine and radialArea

ts
function radialLine<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialLineOptions<TDatum>,
): PolarMark<TDatum>

function radialArea<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialAreaOptions<TDatum>,
): PolarMark<TDatum>

Both marks use angle and radius channels and accept id, className, key, z, color, and a D3 curve factory. The channels default to row index and a numeric datum. color contributes to the chart color scale and defaults to z. When z is omitted, an authored color also partitions the paths. When both are present, z remains the explicit geometry and interaction group. radialLine accepts final stroke, dash, opacity, and optional points styling. radialArea accepts final fill and stroke styling plus radius1 for an explicit inner scale value; radius1 defaults to zero.

Their datum key defaults to a unique top-level or nested data.id, then a unique angle within each effective path group, then row index.

Input order is path order. Use a closed D3 curve such as curveLinearClosed for radar polygons. An explicit z, or color when z is absent, creates one path per group. radialArea can carry its own stroke; layer a closed radialLine only when the outline needs independent styling.

radialDot

ts
function radialDot<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialDotOptions<TDatum>,
): PolarMark<TDatum>

radialDot uses the same angle/radius channel defaults. It also accepts id, className, key, z, color, r, rScale, fill, stroke, and opacity styling. Radius defaults to 3.5 pixels. Each valid datum emits one interaction point with its original angle/radius values and projected screen position. Its key defaults to a unique top-level or nested data.id, then row index.

radialText

ts
function radialText<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialTextOptions<TDatum>,
): PolarMark<TDatum>

radialText maps angle and radius channels through the container's copied polar scales, then positions labels with D3's radial point projection. It accepts text, key, z, color, fill, font size and weight, anchor, baseline, rotation, and pixel dx/dy. radiusOffset is a signed constant or per-datum visual channel applied in pixels after the semantic radius is mapped. It does not contribute to the radius domain. Set anchor: "outside" to resolve start, middle, or end from the final mapped angle. Exact and near-exact top and bottom angles use middle. A nonfinite resolved offset omits that label and its interaction point.

Use it for arc labels, donut-center values, and gauge readouts without leaving the polar coordinate system. Its interaction point follows the final radial offset plus dx/dy while retaining the original semantic radius value. Its key defaults to a unique top-level or nested data.id, then row index.

radialRule

ts
function radialRule<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialRuleOptions<TDatum>,
): PolarMark

radialRule emits one radial segment per datum. angle, radius1, and radius2 are scale values; radius1 defaults to zero. The mark also accepts radius1Offset and radius2Offset as signed constant or per-datum pixel visual channels applied after the corresponding semantic radius is mapped. Offsets never contribute to radius-domain inference. A nonfinite resolved endpoint offset omits that segment. The mark also accepts key, z, color, stroke, opacity, width, and dash styling. It covers gauge needles, ticks, and pie-label leaders without expanding one logical segment into two path rows. Rules remain decorative and emit no interaction points. Its key defaults to a unique top-level or nested data.id, then a unique angle within each z group, then row index.

Pixel offsets do not reserve space outside the polar radius. Use radiusRatio, inset, or chart margins when labels or leaders must remain inside the chart surface.

radialGrid and angleGrid

ts
function radialGrid(options?: RadialGridOptions): PolarGuide
function angleGrid(options?: AngleGridOptions): PolarGuide

radialGrid draws radius values as circles or polygons. Supply explicit values, or let ticks request values from the configured radius scale. Labels are off by default. Label angle, offset, rotation, format, fill, and font size are configurable.

angleGrid draws spokes for explicit values or the configured angle domain. It can show labels around the circumference with format and labelOffset. Labels are on by default and use the same outside-anchor rule as radialText({ anchor: "outside" }) unless labelAnchor is supplied. Both guides accept ID, class, stroke, opacity, width, and dash styling.

Guide label position and orientation can be constants or callbacks through PolarGuideLabelOption. Each callback receives a PolarGuideLabelContext with the semantic value, index, angle, radius, local x/y position, and complete layout. Use labelAnchor, labelBaseline, labelDx, labelDy, and labelRotate without rebuilding the guide. labelClassName targets the label group. Guides are decorative and emit no interaction points.

Every guide returns a PolarGuideScene:

ts
interface PolarGuideScene {
  background: readonly SceneNode[]
  foreground?: readonly SceneNode[]
}

polar collects every guide background in declaration order, renders all marks, then appends every optional foreground in the same guide order. The built-in grids put rings and spokes in background and labels in foreground, keeping labels legible without painting grid geometry over the data.

The exported option contracts are PolarOptions, RadialArcOptions, RadialBarRadiusOptions, RadialBarAngleOptions, RadialLineOptions, RadialAreaOptions, RadialDotOptions, RadialTextOptions, RadialRuleOptions, RadialGridOptions, and AngleGridOptions. The coordinate contracts are PolarAngleOptions, PolarRadiusOptions, PolarResolvedScale, PolarLayoutContext, PolarLength, PolarGuideLabelContext, PolarGuideLabelOption, PolarMark, PolarGuide, and PolarGuideScene. PolarMark and PolarGuide annotate built-in constructor results rather than a supported custom-extension boundary.

See Polar and Radar Charts for pie, donut, gauge, radar, radial bar, numeric line, and numeric scatter compositions.