DocumentationComponentsTheme CreatorGitHub
Theme CreatorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleChatBubbleNewCheckboxChipCoachMarkCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPickerPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderCircularSliderMediaTrimmerSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupTokenizerToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Radar Chart

Token-driven radar (spider) chart built on Recharts v3 for comparing multiple metrics across one or more series, with full light/dark theming.

Playground

Installation

pnpm add @tessinaui/ui recharts

Usage

import { RadarChart } from "@tessinaui/ui";
import type { ChartConfig } from "@tessinaui/ui";

const config: ChartConfig = {
  productA: { label: "Product A", color: "var(--chart-1)" },
  productB: { label: "Product B", color: "var(--chart-3)" },
};

const data = [
  { metric: "Speed", productA: 120, productB: 90 },
  { metric: "Reliability", productA: 98, productB: 130 },
  { metric: "Comfort", productA: 86, productB: 130 },
];

<RadarChart data={data} config={config} categoryKey="metric" />

Examples

Default

A single-series radar comparing one product across six metrics.

Multiple series

Two radars overlaid in distinct brand tones, with a legend.

Sizes

Three square size caps — sm, md, and lg.

Lines only

Outline-only radar with fillOpacity={0} so the shape reads as a stroke.

Grid types

Four grids: concentric polygons (default) or circles via gridType, rings without spokes via radialLines={false}, and no grid at all.

Radius axis and a pinned scale

showRadiusAxis puts the value scale on the chart, and maxValue pins the outer ring. Without it the outer ring is the largest value in your data, so a dataset topping out at 72 paints a full-radius polygon and reads as a perfect score.

Values on the axis labels

Pass a PolarAngleAxis through children to take over the axis labels — Recharts keys polar axes by id, so yours replaces the chart's own rather than drawing a second one on top.

Icon labels

The same seam, with a decorative glyph above each axis name. Keep the name as text: an icon alone is not readable to assistive tech or at 200% zoom.

Filtering series

series is the filter — pass the keys you want plotted and the chart draws exactly those. No extra API is needed for a rater or category picker.

States

The loading skeleton alongside the empty state.

Core props

PropTypeDefaultDescription
dataRecord<string, string | number | null>[]—One row per axis (spoke).
configChartConfig—Series metadata (label, color).
categoryKeystring—Datum key for the angular axis.
seriesstring[]all config keysWhich series to plot.
fillOpacitynumber0.1Per-series fill opacity.
showDotsbooleantrueVertex markers.
size"sm" | "md" | "lg""md"Square size cap.
radialLinesbooleantrueDraw the spokes running out to each axis.
showRadiusAxisbooleanfalseShow the radial value scale.
maxValuenumberdata maxPin the radial scale to [0, maxValue].
categoryFormatter(value: string) => string—Format the axis (spoke) labels.
valueFormatter(value, index) => string—Format radial tick text, e.g. "70%".
showGrid / showLegend / showTooltipboolean—Toggle chart chrome.
labelstring—Accessible name — renders role="figure". State the insight.
descriptionstring—Visually hidden summary read after the label.
childrenReactNode—Recharts children rendered inside the chart.
loadingbooleanfalseRender the skeleton.
errorReactNode—Render an error message instead of the chart.
emptyStateReactNode"No data to display"Shown when data is empty.

When to use a radar chart

A radar is good at shape — a profile you can recognise and compare at a glance. It is poor at magnitude: readers judge radial length less accurately than aligned length, and a filled area grows with the square of its values, so a series twice as high looks four times as big. If the question is "which is larger, and by how much", a grouped bar chart answers it better.

  • Three axes minimum, eight is a comfortable maximum. Below three the polygon degenerates; the largest we found in the wild is a product that caps itself at twelve.
  • Axis order changes the picture. The same numbers make a different shape when reordered, so choose an order that means something (a workflow, a ranking, a fixed template) and keep it stable across charts.
  • Three series is the practical ceiling. Past that, filled areas hide one another — use fillOpacity={0} for outlines, or split into small multiples.
  • Give every chart a label. It names the figure to assistive tech; description carries the numbers a sighted reader gets from the shape.
  • All axes share one scale. If your metrics are not comparable, normalise them before plotting (and say so in the description) or use another chart.

Keyboard and assistive tech

Recharts' accessibilityLayer is on: the chart takes focus and the arrow keys step between points, announcing each through the tooltip's polite live region. VoiceOver users need QuickNav off for the arrow keys to reach the chart.

PromptInputRadial Chart

On this page

PlaygroundInstallationUsageExamplesDefaultMultiple seriesSizesLines onlyGrid typesRadius axis and a pinned scaleValues on the axis labelsIcon labelsFiltering seriesStatesCore propsWhen to use a radar chartKeyboard and assistive tech