Skip to content

Radar chart

TIP

For core principles, concept description, API and changelog, refer to the D3 chart.

Basic usage

tsx
import { Chart, colors } from '@semcore/ui/d3-chart';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  return (
    <Chart.Radar
      data={data}
      groupKey='categories'
      plotWidth={400}
      plotHeight={400}
      aria-label='Radar chart'
    />
  );
};

const data = RadarMockData.Default;

export default Demo;

Scale

You must pass a scale with a specified domain, range doesn't need to be specified as it is calculated automatically. You can modify the range or use a non-linear scale.

tsx
import { Flex } from '@semcore/ui/base-components';
import { Plot, Radar } from '@semcore/ui/d3-chart';
import { scaleLinear } from 'd3-scale';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  const width = 250;
  const height = 250;

  const scale_1 = scaleLinear().domain([0, 10]);
  const scale_2 = scaleLinear().domain([0, 20]);

  return (
    <Flex>
      <Plot data={data} width={width} height={height}>
        <Radar scale={scale_1}>
          <Radar.Axis dataKey='categories'>
            <Radar.Axis.Ticks />
            <Radar.Axis.Labels />
          </Radar.Axis>
          <Radar.Polygon dataKey='data_1'>
            <Radar.Polygon.Line />
            <Radar.Polygon.Dots />
          </Radar.Polygon>
        </Radar>
      </Plot>
      <Plot data={data} width={width} height={height}>
        <Radar scale={scale_2}>
          <Radar.Axis dataKey='categories'>
            <Radar.Axis.Ticks />
            <Radar.Axis.Labels />
          </Radar.Axis>
          <Radar.Polygon dataKey='data_1'>
            <Radar.Polygon.Line />
            <Radar.Polygon.Dots />
          </Radar.Polygon>
        </Radar>
      </Plot>
    </Flex>
  );
};

const data = RadarMockData.SingleData;

export default Demo;

Color

You can change the color by passing the color property to the <Radar.Polygon/>. It is also possible to pass the 'color' property to <Radar.Polygon.Line/> and <Radar.Polygon.Dots/>.

tsx
import { Plot, Radar } from '@semcore/ui/d3-chart';
import { scaleLinear } from 'd3-scale';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  const width = 500;
  const height = 500;

  const scale = scaleLinear().domain([0, 10]);

  return (
    <Plot data={data} width={width} height={height}>
      <Radar scale={scale}>
        <Radar.Axis dataKey='categories'>
          <Radar.Axis.Ticks />
          <Radar.Axis.Labels />
        </Radar.Axis>
        <Radar.Polygon dataKey='data_1' color='chart-palette-order-1'>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
        <Radar.Polygon dataKey='data_2' color='chart-palette-order-2'>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
      </Radar>
    </Plot>
  );
};

const data = RadarMockData.Default;

export default Demo;

Background color

You can use the fill="transparent" property to make polygons transparent.

tsx
import { Plot, Radar, colors } from '@semcore/ui/d3-chart';
import { scaleLinear } from 'd3-scale';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  const width = 500;
  const height = 500;

  const scale = scaleLinear().domain([0, 10]);

  return (
    <Plot data={data} width={width} height={height}>
      <Radar scale={scale}>
        <Radar.Axis dataKey='categories'>
          <Radar.Axis.Ticks />
          <Radar.Axis.Labels />
        </Radar.Axis>
        <Radar.Polygon dataKey='data_1' color='chart-palette-order-1' fill='transparent'>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
        <Radar.Polygon dataKey='data_2' color='chart-palette-order-2' fill='transparent'>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
      </Radar>
    </Plot>
  );
};

const data = RadarMockData.Default;

export default Demo;

Label long

If your labels are too long, you can move them to the next line using the line break symbol \n.

tsx
import { Flex } from '@semcore/ui/base-components';
import { Plot, Radar } from '@semcore/ui/d3-chart';
import { scaleLinear } from 'd3-scale';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  const width = 250;
  const height = 250;

  const scale = scaleLinear().domain([0, 10]);

  return (
    <Flex>
      <Plot data={data} width={width} height={height}>
        <Radar scale={scale}>
          <Radar.Axis dataKey='categories'>
            <Radar.Axis.Ticks />
            <Radar.Axis.Labels />
          </Radar.Axis>
          <Radar.Polygon dataKey='data_1'>
            <Radar.Polygon.Line />
            <Radar.Polygon.Dots />
          </Radar.Polygon>
        </Radar>
      </Plot>
    </Flex>
  );
};

const data = RadarMockData.LongLabel;

export default Demo;

Label custom

If you need a custom React component instead of a label, you can change the display in the render function.

tsx
import { Flex } from '@semcore/ui/base-components';
import { Plot, Radar, getLabelOffsetPosition } from '@semcore/ui/d3-chart';
import Tag from '@semcore/ui/tag';
import { scaleLinear } from 'd3-scale';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  const width = 250;
  const height = 250;

  const scale = scaleLinear().domain([0, 10]);
  const maxLabelWidth = 50;

  return (
    <Flex>
      <Plot data={data} width={width} height={height}>
        <Radar scale={scale} offset={maxLabelWidth}>
          <Radar.Axis dataKey='categories'>
            <Radar.Axis.Ticks />
            <Radar.Axis.Labels>
              {(props) => {
                const width = maxLabelWidth;
                const height = 20;
                const [xOffset, yOffset] = getLabelOffsetPosition(
                  props.xDirection,
                  props.yDirection,
                  width,
                  height,
                );
                return {
                  tag: 'g',
                  children: (
                    <foreignObject
                      x={props.x - xOffset}
                      y={props.y - yOffset}
                      width={width}
                      height={height}
                    >
                      <Tag>{props.children}</Tag>
                    </foreignObject>
                  ),
                };
              }}
            </Radar.Axis.Labels>
          </Radar.Axis>
          <Radar.Polygon dataKey='data_1'>
            <Radar.Polygon.Line />
            <Radar.Polygon.Dots />
          </Radar.Polygon>
        </Radar>
      </Plot>
    </Flex>
  );
};

const data = RadarMockData.SingleData;

export default Demo;

Tooltip

You need to use the <Radar.Tooltip /> component to add interactivity.

tsx
import { Plot, Radar } from '@semcore/ui/d3-chart';
import { scaleLinear } from 'd3-scale';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  const width = 500;
  const height = 500;

  const scale = scaleLinear().domain([0, 10]);

  return (
    <Plot data={data} width={width} height={height}>
      <Radar scale={scale}>
        <Radar.Axis dataKey='categories'>
          <Radar.Axis.Ticks />
          <Radar.Axis.Labels />
        </Radar.Axis>
        <Radar.Tooltip wMin={100}>
          {({ index }) => {
            return {
              children: (
                <>
                  <Radar.Tooltip.Title>{data.categories[index]}</Radar.Tooltip.Title>
                  <Radar.Tooltip.Dot>{data['data_1'][index]}</Radar.Tooltip.Dot>
                  <Radar.Tooltip.Dot>{data['data_2'][index]}</Radar.Tooltip.Dot>
                </>
              ),
            };
          }}
        </Radar.Tooltip>
        <Radar.Polygon dataKey='data_1'>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
        <Radar.Polygon dataKey='data_2'>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
      </Radar>
    </Plot>
  );
};

const data = RadarMockData.Default;

export default Demo;

Circle

To make the chart round, you need to pass the parameter type="circle". You can also round the polygons by passing the "curve" parameter from D3 into them.

tsx
import { Plot, Radar } from '@semcore/ui/d3-chart';
import { scaleLinear } from 'd3-scale';
import { curveCardinalClosed } from 'd3-shape';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  const width = 500;
  const height = 500;

  const scale = scaleLinear().domain([0, 10]);

  return (
    <Plot data={data} width={width} height={height}>
      <Radar scale={scale} type='circle'>
        <Radar.Axis dataKey='categories'>
          <Radar.Axis.Ticks />
          <Radar.Axis.Labels />
        </Radar.Axis>
        <Radar.Tooltip wMin={100}>
          {({ index }) => {
            return {
              children: (
                <>
                  <Radar.Tooltip.Title>{data.categories[index]}</Radar.Tooltip.Title>
                  <Radar.Tooltip.Dot>{data['data_1'][index]}</Radar.Tooltip.Dot>
                  <Radar.Tooltip.Dot>{data['data_2'][index]}</Radar.Tooltip.Dot>
                </>
              ),
            };
          }}
        </Radar.Tooltip>
        <Radar.Polygon dataKey='data_1' curve={curveCardinalClosed}>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
        <Radar.Polygon dataKey='data_2' curve={curveCardinalClosed}>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
      </Radar>
    </Plot>
  );
};

const data = RadarMockData.Default;

export default Demo;

Tick size

To change the distance between the grid lines, you need to change the value of the tickSize parameter.

tsx
import { Plot, Radar } from '@semcore/ui/d3-chart';
import { scaleLinear } from 'd3-scale';
import React from 'react';

import RadarMockData from './mock';

const Demo = () => {
  const width = 500;
  const height = 500;

  const scale = scaleLinear().domain([0, 10]);

  return (
    <Plot data={data} width={width} height={height}>
      <Radar scale={scale}>
        <Radar.Axis dataKey='categories'>
          <Radar.Axis.Ticks tickSize={30} />
          <Radar.Axis.Labels />
        </Radar.Axis>
        <Radar.Polygon dataKey='data_1'>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
        <Radar.Polygon dataKey='data_2'>
          <Radar.Polygon.Line />
          <Radar.Polygon.Dots />
        </Radar.Polygon>
      </Radar>
    </Plot>
  );
};

const data = RadarMockData.Default;

export default Demo;

Rotated

To change base angle of the chart, set angleOffset (in radians) parameter.

tsx
import { Plot, Radar } from '@semcore/ui/d3-chart';
import Slider from '@semcore/ui/slider';
import { scaleLinear } from 'd3-scale';
import { curveCardinalClosed } from 'd3-shape';
import React from 'react';

import RadarMockData from './mock';

const scale = scaleLinear().domain([0, 10]);
const Demo = () => {
  const width = 500;
  const height = 500;
  const [angleDegOffset, setAngleDegOffset] = React.useState(45);

  const angleOffset = React.useMemo(() => (angleDegOffset / 180) * Math.PI, [angleDegOffset]);

  return (
    <div>
      <Slider
        value={angleDegOffset}
        onChange={setAngleDegOffset}
        step={1}
        min={-360}
        max={360}
        w={360}
        id='angle-slider'
      />
      <div>
        <label htmlFor='angle-slider'>Angle:</label>
        {' '}
        {angleOffset.toFixed(2)}
        {' '}
        rad (
        {angleDegOffset.toFixed(0)}
        {' '}
        deg)
      </div>
      <Plot data={data} width={width} height={height}>
        <Radar scale={scale} type='circle' angleOffset={angleOffset}>
          <Radar.Axis dataKey='categories'>
            <Radar.Axis.Ticks />
            <Radar.Axis.Labels />
          </Radar.Axis>
          <Radar.Tooltip wMin={100}>
            {({ index }) => {
              return {
                children: (
                  <>
                    <Radar.Tooltip.Title>{data.categories[index]}</Radar.Tooltip.Title>
                    <Radar.Tooltip.Dot>{data['data_1'][index]}</Radar.Tooltip.Dot>
                    <Radar.Tooltip.Dot>{data['data_2'][index]}</Radar.Tooltip.Dot>
                  </>
                ),
              };
            }}
          </Radar.Tooltip>
          <Radar.Polygon dataKey='data_1' curve={curveCardinalClosed}>
            <Radar.Polygon.Line />
            <Radar.Polygon.Dots />
          </Radar.Polygon>
          <Radar.Polygon dataKey='data_2' curve={curveCardinalClosed}>
            <Radar.Polygon.Line />
            <Radar.Polygon.Dots />
          </Radar.Polygon>
        </Radar>
      </Plot>
    </div>
  );
};

const data = RadarMockData.Default;

export default Demo;

Legend and pattern fill

Note that for ChartLegend patterns property works only with default shape={'Checkbox'}.

tsx
import { Plot, Radar, colors, ChartLegend } from '@semcore/ui/d3-chart';
import { scaleLinear } from 'd3-scale';
import React from 'react';

import RadarMockData from './mock';

type DataKey = keyof typeof data;

const getDefaultLegendItems = () => {
  return (Object.keys(data) as DataKey[])
    .filter((name) => name !== 'categories')
    .map((item, index) => {
      return {
        id: item,
        label: `Category ${index + 1}`,
        data: data[item],
        checked: true,
        color: lineColors[item],
      };
    });
};

const Demo = () => {
  const [legendItems, setLegendItems] = React.useState(getDefaultLegendItems);

  const handleChangeVisible = React.useCallback((id: string, isVisible: boolean) => {
    setLegendItems((prevItems) =>
      prevItems.map((item) =>
        item.id === id ? { ...item, checked: isVisible } : item,
      ),
    );
  }, []);

  const width = 500;
  const height = 500;

  const scale = scaleLinear().domain([0, 10]);

  return (
    <>
      <ChartLegend
        items={legendItems}
        patterns
        aria-label='Radar chart legend'
        onChangeVisibleItem={handleChangeVisible}
      />
      <Plot data={data} width={width} height={height} patterns>
        <Radar scale={scale}>
          <Radar.Axis dataKey='categories'>
            <Radar.Axis.Ticks />
            <Radar.Axis.Labels />
          </Radar.Axis>

          {legendItems.map(
            (item) =>
              item.checked && (
                <Radar.Polygon key={item.id} dataKey={item.id} color={item.color}>
                  <Radar.Polygon.Line />
                  <Radar.Polygon.Dots />
                </Radar.Polygon>
              ),
          )}
        </Radar>
      </Plot>
    </>
  );
};

const data = RadarMockData.Default;

const lineColors: Record<keyof typeof data, string> = {
  categories: '',
  data_1: colors['orange-04'],
  data_2: colors['violet-04'],
};

export default Demo;

Released under the MIT License.

Released under the MIT License.