Antares
ComponentsChart

Bar Chart

A flexible, accessible, and design-aligned data visualization solution for displaying single or multi-series bar charts.

Features

  • Single and multi-series support: Display one or multiple data series with grouped bars
  • Vertical and horizontal orientations: Choose the best layout for your data and use case
  • Responsive behavior: Auto-sizing with horizontal scrolling for many data points, sticky axes
  • Design system alignment: 8px border radius, 12px bar width (1.5 GU), consistent spacing
  • Interactive tooltips: Hover to see exact values, with smart positioning and scroll-awareness
  • Customizable axes: Control gridlines, tick marks, axis titles, and tick formatting
  • Custom domains: Set explicit x and y ranges for controlled scales across multiple charts
  • RTL support: Full right-to-left layout support for international audiences (in development)
  • Accessibility ready: Keyboard navigation, screen reader support, and semantic HTML
  • TypeScript support: Fully typed with comprehensive prop interfaces
  • VisX integration: Leverages VisX primitives for scales, axes, shapes, and responsive layout

Installation

npm install --save @godaddy/antares

Props

The BarChart component accepts the following props:

NameTypeDefault
seriesSeriesConfig<T>[]

Configuration for data series. For a single series, provide an array with one element. For multiple series, provide multiple elements.

orientation?"vertical" | "horizontal" | undefined'vertical'

Orientation of the bars. - 'vertical': Bars extend upward from the X-axis - 'horizontal': Bars extend rightward from the Y-axis

xAxisTitle?string | undefined

Title displayed on the X-axis

xLabels?boolean | undefinedtrue

Whether to show X-axis labels.

xLabelsOrientation?XLabelsOrientation | undefined'auto'

X-axis label orientation. 'auto' rotates labels to vertical when the container is too narrow; 'horizontal' keeps labels horizontal (chart may scroll); 'vertical' keeps labels vertical.

xTickMarks?boolean | undefined

Whether to show X-axis tick marks.

xBaseline?boolean | undefined

Whether to show the X-axis baseline (axis line).

xGridlines?boolean | undefined

Whether to show vertical gridlines for the X-axis.

xTickFormat?((value: number | string | Date) => string) | undefined

Custom formatting function for X-axis tick labels. Receives the tick value and returns a formatted string. The value type depends on orientation: - Vertical: categories (string | Date) from xAccessor - Horizontal: numeric values (number) from xAccessor

xDomain?string[] | [number, number] | undefined

Domain values for the X-axis. Type: string[] | [number, number] - Vertical orientation: Use string[] or Date[] to explicitly set categories - Horizontal orientation: Use [number, number] to set value range [min, max] If not provided, domain is derived from the data.

xNumTicks?number | undefined

Approximate number of ticks to display on the X-axis. Note: This is approximate - the actual number may vary based on scale calculations.

yAxisTitle?string | undefined

Title displayed on the Y-axis

yLabels?boolean | undefinedtrue

Whether to show Y-axis labels.

yTickMarks?boolean | undefined

Whether to show Y-axis tick marks.

yBaseline?boolean | undefined

Whether to show the Y-axis baseline (axis line).

yGridlines?boolean | undefinedtrue

Whether to show horizontal gridlines for the Y-axis.

yTickFormat?((value: number | string | Date) => string) | undefined

Custom formatting function for Y-axis tick labels. Receives the tick value and returns a formatted string. The value type depends on orientation: - Vertical: numeric values (number) from yAccessor - Horizontal: categories (string | Date) from yAccessor

yDomain?string[] | [number, number] | undefined

Domain values for the Y-axis. Type: [number, number] | string[] - Vertical orientation: Use [number, number] to set value range [min, max] - Horizontal orientation: Use string[] or Date[] to explicitly set categories If not provided, domain is derived from the data.

yNumTicks?number | undefined

Approximate number of ticks to display on the Y-axis. Note: This is approximate - the actual number may vary based on scale calculations.

legendPosition?LegendPosition | null | undefined'bottom' when more than one series; hidden otherwise

Legend position. Omit for default: shown at bottom when there is more than one series, hidden for a single series. Set to `null` to hide the legend.

tooltip?boolean | undefinedtrue

Whether to show the tooltip popover on hover. When false, the tooltip is hidden.

width?number | undefined

Outer container width (omitted = 100%)

height?number | undefined

Outer container height (omitted = 100%)

desc?string | undefined

Detailed description of the chart for screen readers. Provides additional context beyond the aria-label. This is rendered as a <desc> element inside the SVG for screen reader support.

className?string | undefined

Additional CSS class name

xAccessor?(((datum: T) => number | string | Date) & ((datum: DataPoint & object) => number | string | Date)) | (((datum: T) => number | string | Date) & ((datum: object) => number | string | Date)) | undefined

Accessor function to extract X values from data points

yAccessor?(((datum: T) => number | string | Date | null) & ((datum: DataPoint & object) => number | string | Date | null)) | (((datum: T) => number | string | Date | null) & ((datum: object) => number | string | Date | null)) | undefined

Accessor function to extract Y values from data points

Core Props

series (required)

An array of data series to display. Each series must include:

  • id: Unique identifier for the series
  • name: Display name for the legend
  • data: Array of data points
  • color: Hex color for the bars
const series = [
  {
    id: 'new-york',
    name: 'New York',
    color: '#145FA9',
    data: [{ date: '2024-01-01', temp: 45 }, ...]
  }
];

xAccessor (required)

Function that extracts the x-axis value from a data point. Returns a string representing the category or label.

xAccessor={(d) => d.date}

yAccessor (required)

Function that extracts the y-axis value from a data point. Returns a number representing the value to display.

yAccessor={(d) => d.temperature}

Layout Props

orientation

Controls whether bars are vertical (default) or horizontal.

  • Default: 'vertical'
  • Options: 'vertical' | 'horizontal'

Vertical charts are best for time series or categorical data. Horizontal charts work well for rankings or when category labels are long.

width

Width of the chart in pixels. The chart uses responsive sizing via useParentSize, so this serves as a default when the container size is not yet known.

  • Default: 500

height

Height of the chart in pixels.

  • Default: 700

legendPosition

Position of the legend relative to the chart.

  • Default: 'bottom'
  • Options: 'top' | 'bottom'

Axis Props

xAxisTitle

Title displayed below the x-axis.

  • Type: string

yAxisTitle

Title displayed to the left of the y-axis.

  • Type: string

xBaseline

Whether to show the x-axis baseline and labels.

  • Default: true

yBaseline

Whether to show the y-axis baseline and labels.

  • Default: true

xTickMarks

Whether to show tick marks on the x-axis.

  • Default: true

yTickMarks

Whether to show tick marks on the y-axis.

  • Default: true

xGridlines

Whether to show vertical gridlines from the x-axis.

  • Default: true

yGridlines

Whether to show horizontal gridlines from the y-axis.

  • Default: true

xTickFormat

Function to format x-axis tick labels. The value type depends on orientation:

  • Vertical: categories (string | Date) from xAccessor

  • Horizontal: numeric values (number) from xAccessor

  • Type: (value: string | number | Date) => string

yTickFormat

Function to format y-axis tick labels. The value type depends on orientation:

  • Vertical: numeric values (number) from yAccessor

  • Horizontal: categories (string | Date) from yAccessor

  • Type: (value: string | number | Date) => string

xNumTicks

Suggested number of ticks for the x-axis.

  • Type: number

yNumTicks

Suggested number of ticks for the y-axis.

  • Type: number

Domain Props

xDomain

Explicit x-axis domain. The type depends on orientation:

  • Vertical: Use string[] or Date[] to explicitly set categories
  • Horizontal: Use [number, number] to set value range [min, max]

Useful for ensuring consistent domains across multiple charts or showing empty slots for missing data.

  • Type: string[] | [number, number]
// Vertical: Show slots for categories A-F even if some are missing from data
xDomain={['A', 'B', 'C', 'D', 'E', 'F']}

// Horizontal: Set value range
xDomain={[0, 100]}

yDomain

Explicit y-axis domain. The type depends on orientation:

  • Vertical: Use [number, number] to set value range [min, max]
  • Horizontal: Use string[] or Date[] to explicitly set categories

Useful for consistent scales across multiple charts or emphasizing specific value ranges.

  • Type: [number, number] | string[]
// Vertical: Always show 0-100 range regardless of data
yDomain={[0, 100]}

// Horizontal: Show slots for categories A-F
yDomain={['A', 'B', 'C', 'D', 'E', 'F']}

Interaction Props

tooltip

Whether to show tooltips on hover.

  • Default: true

Basic Usage

Single Series

Display a single data series as vertical bars:

import { BarChart } from '@godaddy/antares';import { cityTemperature } from '@visx/mock-data';export function BarChartExample(props: any) {  const series = [    {      id: 'new-york-series',      name: 'New York',      data: cityTemperature.slice(0, 10)    }  ];  return (    <BarChart      series={series}      xAccessor={(d: any) => d.date}      yAccessor={(d: any) => d['New York']}      xAxisTitle="Date"      yAxisTitle="Temperature (°F)"      xBaseline={true}      yBaseline={true}      xTickMarks={true}      yTickMarks={true}      yGridlines={true}      xGridlines={true}      {...props}    />  );}
Temperature (°F)
Date

Multi-Series

Display multiple data series as grouped bars with a legend:

import { BarChart } from '@godaddy/antares';import { cityTemperature } from '@visx/mock-data';export function BarChartMultiSeriesExample(props: any) {  const cities = ['New York', 'San Francisco', 'Austin'] as const;  const series = cities.map(function mapCity(city, index) {    return {      id: `city-${city.toLowerCase().replace(/\s+/g, '-')}`,      name: city,      data: cityTemperature.slice(0, 10).map(function mapData(d) {        return {          x: d.date,          y: parseFloat(d[city as keyof typeof d])        };      })    };  });  return (    <BarChart      series={series}      xAccessor={(d: any) => d.x}      yAccessor={(d: any) => d.y}      xAxisTitle="Date"      yAxisTitle="Temperature (°F)"      yDomain={[0, 100]}      xBaseline={true}      yBaseline={true}      xTickMarks={true}      yTickMarks={true}      {...props}    />  );}
Temperature (°F)
Date
New York
San Francisco
Austin

Orientation

Horizontal Single Series

Horizontal bars are ideal for ranking, comparison, or when you have long category labels:

import { BarChart } from '@godaddy/antares';import { exoplanets as allExoplanets, type Exoplanets } from '@visx/mock-data';const largestExoplanets = [...allExoplanets.filter((d) => d.distance && d.distance > 0)]  .sort((a, b) => b.radius - a.radius)  .slice(0, 30);const series = [{ id: 'radius', name: 'Radius', data: largestExoplanets }];export function BarChartHorizontalSingleSeriesExample(props: any) {  return (    <BarChart      series={series}      orientation="horizontal"      xAccessor={(d: Exoplanets) => d.radius}      yAccessor={(d: Exoplanets) => d.name}      xAxisTitle="Radius (Rj)"      yAxisTitle="Exoplanet"      xBaseline={true}      yBaseline={true}      xTickMarks={true}      yTickMarks={true}      yGridlines={true}      xGridlines={true}      {...props}    />  );}
Exoplanet
Radius (Rj)

Horizontal Multi-Series

Grouped bars work in horizontal orientation as well:

import { BarChart } from '@godaddy/antares';import { exoplanets as allExoplanets } from '@visx/mock-data';interface ExoplanetSurveyPoint {  name: string;  value: number;}// Take 90 real exoplanets and use the first 15 names as the shared category axis.// Each series maps a different group of 15 planets' radius onto those same category names,// demonstrating multi-series grouped bars with real exoplanet data.const trueExoplanets = allExoplanets.filter((d) => d.distance && d.distance > 0).slice(0, 60);const categoryNames = trueExoplanets.slice(0, 15).map((d) => d.name);function toSurveyData(planets: typeof trueExoplanets): ExoplanetSurveyPoint[] {  return planets.slice(0, 15).map((d, i) => ({ name: categoryNames[i], value: d.radius }));}const series = [  { id: 'survey-a', name: 'Survey A', data: toSurveyData(trueExoplanets.slice(0, 15)) },  { id: 'survey-b', name: 'Survey B', data: toSurveyData(trueExoplanets.slice(15, 30)) },  { id: 'survey-c', name: 'Survey C', data: toSurveyData(trueExoplanets.slice(30, 45)) },  { id: 'survey-d', name: 'Survey D', data: toSurveyData(trueExoplanets.slice(45, 60)) }];export function BarChartHorizontalMultiSeriesExample(props: any) {  return (    <BarChart      series={series}      orientation="horizontal"      xAccessor={(d: ExoplanetSurveyPoint) => d.value}      yAccessor={(d: ExoplanetSurveyPoint) => d.name}      xAxisTitle="Radius (Rj)"      yAxisTitle="Exoplanet"      xBaseline={true}      yBaseline={true}      xTickMarks={true}      yTickMarks={true}      yGridlines={true}      xGridlines={true}      {...props}    />  );}
Exoplanet
Radius (Rj)
Survey A
Survey B
Survey C
Survey D

Custom Domains

Control the exact categories and value ranges displayed:

import { BarChart } from '@godaddy/antares';export function BarChartCustomDomainExample() {  const data = [    { category: 'A', value: 10 },    { category: 'B', value: 20 },    { category: 'C', value: 15 },    { category: 'D', value: 30 },    { category: 'E', value: 5 }  ];  // Example: set xDomain and yDomain explicitly  const xDomain = ['A', 'B', 'D', 'E', 'F', 'C']; // F will show as empty slot  const yDomain: [number, number] = [0, 40];  return (    <BarChart      series={[        {          id: 'series1',          name: 'Sample Series',          data        }      ]}      xAccessor={(d: { category: string; value: number }) => d.category}      yAccessor={(d: { category: string; value: number }) => d.value}      height={400}      width={600}      xAxisTitle="Category"      yAxisTitle="Value"      xDomain={xDomain}      yDomain={yDomain}    />  );}
Value
Category

In this example:

  • xDomain includes category 'F' which doesn't exist in the data, showing an empty slot
  • yDomain is set to [0, 40] to maintain a consistent scale even though the max value is 30

RTL Support

The chart follows the current layout direction (LTR or RTL). By default, that direction is detected automatically from the browser or system settings. When it is RTL, the chart mirrors axes, bars, and labels accordingly.

import { BarChart } from '@godaddy/antares';import { cityTemperature } from '@visx/mock-data';import { RTLProvider } from '../../../../utils/rtl-locale-provider.tsx';export function BarChartRTLMultiSeriesExample(props: any) {  const cities = ['New York', 'San Francisco', 'Austin'] as const;  const series = cities.map(function mapCity(city) {    return {      id: `city-${city.toLowerCase().replace(/\s+/g, '-')}`,      name: city,      data: cityTemperature.slice(0, 10).map(function mapData(d) {        return {          x: d.date,          y: parseFloat(d[city as keyof typeof d])        };      })    };  });  return (    <RTLProvider>      <BarChart        series={series}        xAccessor={(d: any) => d.x}        yAccessor={(d: any) => d.y}        xAxisTitle="Date"        yAxisTitle="Temperature (°F)"        xBaseline={true}        yBaseline={true}        xTickMarks={true}        yTickMarks={true}        yGridlines={true}        {...props}      />    </RTLProvider>  );}
Temperature (°F)
Date
New York
San Francisco
Austin

Note: RTL support is currently in active development. Axis rendering and tick mark spacing are being refined.

Design System Alignment

The BarChart component is built to align with Antares design principles:

Spacing and Sizing

  • Bar width: 12px (1.5 grid units)
  • Bar padding (multi-series): 4px between bars within a group
  • Group gap: Minimum 24px between bar groups
  • Border radius: 8px on all bars
  • Margins: 20px top/right, 60px bottom/left for axes and labels

Responsive Behavior

  • Auto-sizing: Uses VisX's useParentSize hook to adapt to container width
  • Horizontal scrolling: When data points exceed available width, the chart becomes horizontally scrollable
  • Sticky y-axis: The y-axis remains visible during horizontal scroll
  • Minimum spacing: Group gaps never fall below 24px, triggering scroll instead

Accessibility

  • Keyboard navigation: Bar groups are keyboard accessible with tabIndex={0} and role="group"
  • Screen reader support: Semantic HTML structure with proper ARIA attributes
  • Tooltips: Show on both hover and focus for keyboard users

Performance Considerations

Rendering Strategy

The component uses VisX primitives for optimal SVG rendering:

  • Scales: VisX's scaleBand and scaleLinear for efficient coordinate mapping
  • Grouped rendering: Bars are rendered in SVG <Group> elements for logical organization
  • Responsive sizing: useParentSize hook provides efficient resize detection with debouncing (15ms)

Scrolling Strategy

  • Vertical charts: Horizontal scrolling when many data points exceed container width
  • Horizontal charts: Vertical scrolling when many categories exceed container height
  • Sticky axes: Positioned absolutely to remain visible during scroll

Recommendations

  • For charts with many data points (>20), consider enabling horizontal scroll (default behavior)
  • For horizontal bar charts, vertical scrolling is more mobile-friendly than horizontal
  • Use custom domains to maintain consistent scales across multiple charts
  • Tooltips use absolute positioning and scroll-aware calculations for accurate placement

Development Status

The BarChart component is in active development. Current focus areas:

  • ✅ Single and multi-series support
  • ✅ Vertical and horizontal orientations
  • ✅ Responsive behavior with sticky axes
  • ✅ Interactive tooltips
  • ✅ Custom domains
  • ✅ Design system alignment
  • 🚧 RTL compatibility (axis rendering and spacing refinement)
  • 🚧 Tick mark positioning improvements
  • 🚧 Additional customization options (tick formatting, axis configuration)

Troubleshooting

Bars Not Showing

If bars are not visible:

  1. Check data: Ensure series array has data and xAccessor/yAccessor return valid values
  2. Check domains: Verify yDomain (if set) includes your data values
  3. Check dimensions: Ensure container has non-zero width and height

Tooltip Positioning Issues

If tooltips appear in the wrong location:

  1. Check scroll position: The component accounts for horizontal scroll automatically
  2. Check container: Ensure the chart container is not transformed or positioned in a complex layout
  3. Verify event targets: Tooltips use mouse event coordinates and bar group positions

Performance Issues

For better performance:

  1. Limit data points: Consider pagination or filtering for very large datasets (>100 points)
  2. Debounce resize: The default 15ms debounce on useParentSize balances smoothness and performance
  3. Simplify tooltips: If tooltips are complex, consider using simpler content or disabling them

Examples

All examples can be found in the Storybook interface:

  • Single Series: Basic vertical bar chart
  • Multi-Series: Grouped bars with legend
  • Horizontal Single Series: Horizontal orientation
  • Horizontal Multi-Series: Grouped horizontal bars
  • Custom Domain: Controlled x and y ranges
  • RTL Multi-Series: Multi-series chart in RTL (in development)

On this page