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/antaresProps
The BarChart component accepts the following props:
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 | undefinedtrueWhether 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 | undefinedtrueWhether 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 | undefinedtrueWhether 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 otherwiseLegend 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 | undefinedtrueWhether 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 seriesname: Display name for the legenddata: Array of data pointscolor: 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[]orDate[]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[]orDate[]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} /> );}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} /> );}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} /> );}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} /> );}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} /> );}In this example:
xDomainincludes category 'F' which doesn't exist in the data, showing an empty slotyDomainis 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> );}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
useParentSizehook 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}androle="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
scaleBandandscaleLinearfor efficient coordinate mapping - Grouped rendering: Bars are rendered in SVG
<Group>elements for logical organization - Responsive sizing:
useParentSizehook 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:
- Check data: Ensure
seriesarray has data andxAccessor/yAccessorreturn valid values - Check domains: Verify
yDomain(if set) includes your data values - Check dimensions: Ensure container has non-zero width and height
Tooltip Positioning Issues
If tooltips appear in the wrong location:
- Check scroll position: The component accounts for horizontal scroll automatically
- Check container: Ensure the chart container is not transformed or positioned in a complex layout
- Verify event targets: Tooltips use mouse event coordinates and bar group positions
Performance Issues
For better performance:
- Limit data points: Consider pagination or filtering for very large datasets (>100 points)
- Debounce resize: The default 15ms debounce on
useParentSizebalances smoothness and performance - 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)