Skip to main content
Point
@coinbase/cds-web-visualization@3.4.0-beta.8
Visual markers that highlight specific data values on a chart. Points can be customized with different colors, sizes, and interactivity.
Import
import { Point } from '@coinbase/cds-web-visualization'
SourceView source code
Peer dependencies
  • framer-motion: ^10.18.0
Related components
View as Markdown

Basics

Points are visual markers that highlight specific data values on a chart. They can be used to emphasize important data points, show discrete values, or provide interactive elements.

You can add points using points on Line or LineChart.

Loading...
Live Code
<LineChart
  enableScrubbing
  showArea
  showYAxis
  height={{ base: 150, tablet: 200, desktop: 250 }}
  points
  series={[
    {
      id: 'prices',
      data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
    },
  ]}
  xAxis={{
    /* This prevents points close to the right edge from awkward cutoff when scrubbing */
    range: ({ min, max }) => ({ min, max: max - 8 }),
  }}
  yAxis={{
    showGrid: true,
  }}
>
  <Scrubber />
</LineChart>

You can also add Points directly to a chart.

Loading...
Live Code
function MyChart() {
  const prices = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];

  return (
    <CartesianChart
      height={{ base: 150, tablet: 200, desktop: 250 }}
      series={[
        {
          id: 'prices',
          data: prices,
        },
      ]}
      inset={{
        // Overriding the right offset gives us more space to place this
        right: 32,
      }}
    >
      <YAxis showGrid position="left" tickLabelFormatter={(value) => `$${value}`} />
      {prices.map((price, index) => (
        <Point key={index} dataX={index} dataY={price} label={`$${price}`} labelPosition="right" />
      ))}
    </CartesianChart>
  );
}

Conditional

You can conditionally render points to highlight specific values in your data, such as maximum/minimum values or outliers.

Loading...
Live Code
function AssetPriceWithMinMax() {
  const data = sparklineInteractiveData.hour.map((d) => d.value);

  const minPrice = Math.min(...data);
  const maxPrice = Math.max(...data);

  const formatPrice = useCallback((price: number) => {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD',
    }).format(price);
  }, []);

  return (
    <LineChart
      showArea
      areaType="dotted"
      height={{ base: 150, tablet: 200, desktop: 250 }}
      points={({ dataX, dataY }: PointBaseProps) => {
        const isMin = dataY === minPrice;
        const isMax = dataY === maxPrice;

        if (isMin) {
          return { label: formatPrice(dataY), labelPosition: 'bottom' };
        }

        if (isMax) {
          return { label: formatPrice(dataY), labelPosition: 'top' };
        }
      }}
      series={[
        {
          id: 'btc',
          data: data,
          color: assets.btc.color,
        },
      ]}
      style={{ outlineColor: assets.btc.color }}
    />
  );
};

Interaction

Points can be made interactive by adding click handlers, allowing users to explore data in more detail.

Loading...
Live Code
<LineChart
  showArea
  showYAxis
  height={{ base: 150, tablet: 200, desktop: 250 }}
  points={({ dataX, dataY }) => {
    const months = [
      'Jan',
      'Feb',
      'Mar',
      'Apr',
      'May',
      'Jun',
      'Jul',
      'Aug',
      'Sep',
      'Oct',
      'Nov',
      'Dec',
    ];
    return {
      radius: 4,
      onClick: () => alert(`${months[dataX]}: ${dataY} units sold`),
      accessibilityLabel: `${months[dataX]} sales: ${dataY} units`,
    };
  }}
  series={[
    {
      id: 'sales',
      data: [120, 132, 101, 134, 90, 230, 210, 120, 180, 190, 210, 176],
    },
  ]}
  yAxis={{
    showGrid: true,
    label: 'Sales (units)',
  }}
/>

Styling

Points support customization through various properties including colors, sizes, and labels.

Loading...
Live Code
<LineChart
  showArea
  showYAxis
  height={{ base: 150, tablet: 200, desktop: 250 }}
  points={({ dataX, dataY }) => {
    const isHighPerformance = dataY >= 90;
    const isLowPerformance = dataY < 75;

    return {
      fill: isHighPerformance
        ? 'var(--color-bgPositive)'
        : isLowPerformance
          ? 'var(--color-bgNegative)'
          : 'var(--color-fgPrimary)',
      radius: isHighPerformance ? 6 : 4,
      strokeWidth: 2,
      stroke: 'var(--color-bg)',
      label: isHighPerformance || isLowPerformance ? `${dataY}%` : undefined,
      labelPosition: isHighPerformance ? 'top' : 'bottom',
    };
  }}
  series={[
    {
      id: 'performance',
      data: [65, 70, 72, 85, 88, 92, 78, 82, 90, 95, 91, 94],
    },
  ]}
  xAxis={{
    range: ({ min, max }) => ({ min, max: max - 8 }),
  }}
  yAxis={{
    showGrid: true,
    label: 'Performance Score',
  }}
/>

Labels

You can use labelPosition, labelOffset, and labelFont to adjust Point's label.

Loading...
Live Code
function Scatterplot() {
  const dataPoints = [
    { x: 20, y: 30, label: 'A' },
    { x: 40, y: 65, label: 'B' },
    { x: 60, y: 45, label: 'C' },
    { x: 75, y: 80, label: 'D' },
  ];

  return (
    <CartesianChart
      height={250}
      xAxis={{
        domain: { min: 0, max: 100 },
      }}
      yAxis={{
        domain: { min: 0, max: 100 },
      }}
    >
      <XAxis showLine showTickMarks showGrid />
      <YAxis position="left" showLine showTickMarks showGrid />
      {dataPoints.map((point, index) => (
        <Point
          key={index}
          dataX={point.x}
          dataY={point.y}
          label={point.label}
          labelPosition="top"
          labelOffset={6}
          labelFont="title3"
        />
      ))}
    </CartesianChart>
  );
}

Custom Label Position

You can also use LabelComponent to create custom label components.

Loading...
Live Code
function ScatterplotWithCustomLabels() {
  const dataPoints = [
    { x: 12, y: 34, label: 'A', color: 'var(--color-fgAccent)' },
    { x: 28, y: 67, label: 'B', color: 'var(--color-fgAccent)' },
    { x: 45, y: 23, label: 'C', color: 'var(--color-fgAccent)' },
    { x: 67, y: 89, label: 'D', color: 'var(--color-bgPositive)' },
    { x: 82, y: 76, label: 'E', color: 'var(--color-bgPositive)' },
    { x: 34, y: 91, label: 'F', color: 'var(--color-bgPositive)' },
    { x: 56, y: 45, label: 'G', color: 'var(--color-bgPositive)' },
    { x: 19, y: 12, label: 'H', color: 'var(--color-fgWarning)' },
    { x: 73, y: 28, label: 'I', color: 'var(--color-fgWarning)' },
    { x: 91, y: 54, label: 'J', color: 'var(--color-fgWarning)' },
    { x: 15, y: 58, label: 'K', color: 'var(--color-fgPrimary)' },
    { x: 39, y: 72, label: 'L', color: 'var(--color-fgPrimary)' },
    { x: 88, y: 15, label: 'M', color: 'var(--color-fgPrimary)' },
    { x: 52, y: 82, label: 'N', color: 'var(--color-fgPrimary)' },
  ];

  // Calculate domain based on data
  const xValues = dataPoints.map((p) => p.x);
  const yValues = dataPoints.map((p) => p.y);
  const xMin = Math.min(...xValues);
  const xMax = Math.max(...xValues);
  const yMin = Math.min(...yValues);
  const yMax = Math.max(...yValues);

  // Custom label component that places labels to the top-right
  const TopRightPointLabel = ({ x, y, offset = 0, children }) => {
    return (
      <ChartText
        horizontalAlignment="left"
        verticalAlignment="bottom"
        x={x + offset}
        y={y - offset}
        font="label1"
      >
        {children}
      </ChartText>
    );
  };

  return (
    <CartesianChart
      height={300}
      xAxis={{
        domain: { min: xMin, max: xMax },
        domainLimit: 'nice',
      }}
      yAxis={{
        domain: { min: yMin, max: yMax },
        domainLimit: 'nice',
      }}
    >
      <XAxis showLine showTickMarks showGrid />
      <YAxis position="left" showLine showTickMarks showGrid />
      {dataPoints.map((point, index) => (
        <Point
          key={index}
          dataX={point.x}
          dataY={point.y}
          label={point.label}
          labelOffset={8}
          fill={point.color}
          radius={5}
          LabelComponent={TopRightPointLabel}
        />
      ))}
    </CartesianChart>
  );
}

Is this page useful?

Coinbase Design is an open-source, adaptable system of guidelines, components, and tools that aid the best practices of user interface design for crypto products.