---
title: "Series Bars"
framework: javascript
version: "14.1.0"
---

# Series Bars

Data points can be represented by vertical or horizontal bars in many series types, such as [Bar](https://www.ag-grid.com/charts/javascript/bar-series/), [Range Bar](https://www.ag-grid.com/charts/javascript/range-bar-series/), [Waterfall](https://www.ag-grid.com/charts/javascript/waterfall-series/) and [Box Plot](https://www.ag-grid.com/charts/javascript/box-plot-series/).

Styling and customisation options such as `fill`, `stroke` and `cornerRadius` are configurable within each series. See [API Reference](#api-reference) for details.

## Fixed Width

Use the `width` option to set a fixed pixel width for each bar.

#### Fixed Width

```ts
import {
  AgCartesianChartOptions,
  AgCharts,
  BarSeriesModule,
  CategoryAxisModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
  ScrollbarModule,
} from "ag-charts-enterprise";
import { getData } from "./data";

ModuleRegistry.registerModules([
  BarSeriesModule,
  CategoryAxisModule,
  LegendModule,
  NumberAxisModule,
  ScrollbarModule,
]);

const options: AgCartesianChartOptions = {
  data: getData(),
  title: {
    text: "Quarterly Revenue by Product Line",
  },
  scrollbar: { enabled: true },
  series: [
    {
      type: "bar",
      xKey: "quarter",
      yKey: "software",
      yName: "Software",
      width: 30,
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "hardware",
      yName: "Hardware",
      width: 30,
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "services",
      yName: "Services",
      width: 30,
    },
  ],
  axes: {
    x: {
      type: "category",
    },
    y: {
      type: "number",
      label: {
        formatter: ({ value }) => `$${(value / 1000).toFixed(1)}B`,
      },
    },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);

function toggleFixedWidth() {
  for (const series of options.series ?? []) {
    if (!("width" in series)) continue;
    series.width =
      series.width == null
        ? Number(document.getElementById("fixedWidthSliderValue")!.innerHTML)
        : undefined;
  }

  chart.update(options);
}

function updateFixedWidth(event: any) {
  const value = Number(event.target?.value);
  for (const series of options.series ?? []) {
    if (!("width" in series)) continue;
    series.width = value;
  }

  document.getElementById("fixedWidthSliderValue")!.innerHTML = String(value);
  chart.update(options);
}

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).toggleFixedWidth = toggleFixedWidth;
  (<any>window).updateFixedWidth = updateFixedWidth;
}
```

[Live example: Fixed Width](https://www.ag-grid.com/charts/typescript/bars/examples/fixed-width-clipping)

```js
{
    series: [
        {
            type: 'bar',
            width: 30,
        },
    ],
}
```

In this example:

- Each bar in the series has a fixed width of 30 pixels.
- Toggle the fixed width off to let bars automatically size to fit the series area.
- Use the slider to change the width in pixels.

When using fixed width bars:

- Resizing the chart does not affect the width of the bars.
- The bars will be clipped if the fixed width exceeds the available space in the series area.
- Clipped bars can be viewed using the [Scrollbar](https://www.ag-grid.com/charts/javascript/scrollbar/), [Navigator](https://www.ag-grid.com/charts/javascript/navigator/) or [Zoom](https://www.ag-grid.com/charts/javascript/zoom/) controls.

### Band Alignment

Use the `bandAlignment` option on a [Category](https://www.ag-grid.com/charts/javascript/axes-types/#category), [Unit Time](https://www.ag-grid.com/charts/javascript/axes-time/#unit-time) or [Ordinal Time](https://www.ag-grid.com/charts/javascript/axes-time/#ordinal-time) axis to align fixed width bars.

#### Band Alignment

```ts
import {
  AgBandAlignment,
  AgCartesianChartOptions,
  AgCategoryAxisOptions,
  AgCharts,
  BarSeriesModule,
  CategoryAxisModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";
import { DataType, getData } from "./data";

ModuleRegistry.registerModules([
  BarSeriesModule,
  CategoryAxisModule,
  NumberAxisModule,
]);

const options: AgCartesianChartOptions<DataType> = {
  data: getData(),
  title: {
    text: "Total Visitors to Museums and Galleries",
  },
  footnote: {
    text: "Source: Department for Digital, Culture, Media & Sport",
  },
  series: [
    {
      type: "bar",
      xKey: "quarter",
      yKey: "museums",
      yName: "Museums",
      width: 10,
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "galleries",
      yName: "Galleries",
      width: 10,
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "heritage",
      yName: "Heritage Sites",
      width: 10,
    },
  ],
  axes: {
    x: {
      type: "category",
      bandAlignment: "start",
    },
    y: {
      type: "number",
      title: {
        text: "Total Visitors (Millions)",
      },
    },
  },
  formatter: {
    y(params) {
      const value = params.value as number;
      const millions = value / 1000000;
      const accuracy = ["series-label", "axis-label"].includes(params.source)
        ? 0
        : 1;
      return `${millions.toFixed(accuracy)}M`;
    },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);

function changeBandAlignment(alignment: AgBandAlignment) {
  (options.axes!.x! as AgCategoryAxisOptions).bandAlignment = alignment;

  chart.update(options);
}

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).changeBandAlignment = changeBandAlignment;
}
```

[Live example: Band Alignment](https://www.ag-grid.com/charts/typescript/bars/examples/band-alignment)

```js
{
    axes: {
        x: {
            type: 'category',
            bandAlignment: 'start',
        },
    },
}
```

In this example:

- The category axis has an initial band alignment of `start`.
- Use the buttons to compare other band alignment options.
  - `justify` - bands are sized to fill the chart width, with the bars centred within each band.
  - `start` - bands are sized to fit the bar width and aligned to the start of the axis.
  - `center` - bands are sized to fit the bar width and centred within the chart width.
  - `end` - bands are sized to fit the bar width and aligned to the end of the axis.

## Width Ratio

Use the `widthRatio` option to set the bar width as a proportion of the default width.

#### Width Ratio

```ts
import {
  AgCartesianChartOptions,
  AgCharts,
  CrosshairModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
  RangeBarSeriesModule,
  UnitTimeAxisModule,
} from "ag-charts-enterprise";
import { getData } from "./data";

const data = getData();
ModuleRegistry.registerModules([
  CrosshairModule,
  LegendModule,
  NumberAxisModule,
  RangeBarSeriesModule,
  UnitTimeAxisModule,
]);

const options: AgCartesianChartOptions = {
  title: {
    text: "Australia vs Global Temperature Patterns",
  },
  subtitle: {
    text: "Monthly temperature ranges (2020) showing seasonal variations across regions",
  },
  footnote: {
    text: "Data: World Meteorological Organization. Ranges show typical monthly lows and highs.",
    fontStyle: "italic",
  },
  series: [
    {
      data: data.World,
      type: "range-bar",
      xKey: "month",
      yName: "World",
      yLowKey: "lowTemperature",
      yHighKey: "highTemperature",
      yLowName: "Min Temp",
      yHighName: "Max Temp",
      cornerRadius: 5,
      fill: "transparent",
      strokeWidth: 2,
      strokeOpacity: 0.6,
      highlight: { enabled: false },
    },
    {
      data: data.Australia,
      type: "range-bar",
      xKey: "month",
      yName: "Australia",
      grouped: false,
      widthRatio: 0.4,
      yLowKey: "lowTemperature",
      yHighKey: "highTemperature",
      yLowName: "Min Temp",
      yHighName: "Max Temp",
      cornerRadius: 5,
    },
  ],
  axes: {
    x: {
      type: "unit-time",
      label: {
        formatter: ({ value }) => {
          const date = new Date(value);
          return date.toLocaleDateString("en-US", { month: "short" });
        },
      },
    },
    y: {
      label: {
        formatter: ({ value }) => `${value}°C`,
      },
    },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);

function updateWidthRatio(event: any) {
  const value = Number(event.target?.value);
  (options.series![1] as any).widthRatio = value;

  document.getElementById("widthRatioSliderValue")!.innerHTML = String(value);
  chart.update(options);
}

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).updateWidthRatio = updateWidthRatio;
}
```

[Live example: Width Ratio](https://www.ag-grid.com/charts/typescript/bars/examples/width-ratio)

```js
{
    series: [
        {
            type: 'range-bar',
            grouped: false,
            widthRatio: 0.4,
        },
    ],
}
```

In this example:

- The World series uses the default width ratio of 1.
- The Australia series has an initial width ratio of 0.4.
- Use the slider to change the width ratio.
- This gives an [Actual vs Target](#actual-vs-target-bars) style visualisation, with the "World" series as a background reference.

## Actual vs Target Bars

Bars can be layered to create actual vs target comparisons by using `grouped: false` to overlay series.

#### Actual vs Target

```ts
import {
  AgCartesianChartOptions,
  AgCharts,
  BarSeriesModule,
  CategoryAxisModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-community";
import { getData } from "./data";

ModuleRegistry.registerModules([
  BarSeriesModule,
  CategoryAxisModule,
  LegendModule,
  NumberAxisModule,
]);

const options: AgCartesianChartOptions = {
  data: getData(),
  title: {
    text: "Quarterly Sales vs Target",
  },
  series: [
    {
      type: "bar",
      direction: "horizontal",
      xKey: "quarter",
      yKey: "target",
      yName: "Target",
      grouped: false,
      fillOpacity: 0.3,
      cornerRadius: 3,
      highlight: {
        enabled: false,
      },
    },
    {
      type: "bar",
      direction: "horizontal",
      xKey: "quarter",
      yKey: "actual",
      yName: "Actual",
      grouped: false,
      widthRatio: 0.5,
      cornerRadius: 6,
    },
  ],
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);
```

[Live example: Actual vs Target](https://www.ag-grid.com/charts/typescript/bars/examples/actual-target)

```js
{
    series: [
        {
            type: 'bar',
            yKey: 'target',
            grouped: false,
            fillOpacity: 0.3,
        },
        {
            type: 'bar',
            yKey: 'actual',
            grouped: false,
            widthRatio: 0.5,
        },
    ],
}
```

In this example:

- The Target series uses `grouped: false` to span the full category width as a background bar.
- The Actual series also uses `grouped: false` with a `widthRatio` of 0.5 to appear narrower in front.
- The Target series is specified first in the `series` array so that it appears behind the Actual series.
- The target has reduced `fillOpacity` and highlighting disabled.

### Multiple Metrics

Multiple grouped series can be displayed over a single ungrouped target bar.

#### Multiple Metrics vs Target

```ts
import {
  AgCartesianChartOptions,
  AgCharts,
  BarSeriesModule,
  CategoryAxisModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-community";
import { getData } from "./data";

ModuleRegistry.registerModules([
  BarSeriesModule,
  CategoryAxisModule,
  LegendModule,
  NumberAxisModule,
]);

const options: AgCartesianChartOptions = {
  data: getData(),
  title: {
    text: "Regional Sales vs Target",
  },
  series: [
    {
      type: "bar",
      xKey: "quarter",
      yKey: "target",
      yName: "Target",
      grouped: false,
      fillOpacity: 0.3,
      highlight: {
        enabled: false,
      },
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "europe",
      yName: "Europe",
      widthRatio: 0.8,
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "asia",
      yName: "Asia",
      widthRatio: 0.8,
    },
  ],
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);
```

[Live example: Multiple Metrics vs Target](https://www.ag-grid.com/charts/typescript/bars/examples/targets-comparisons)

In this example:

- The "Target" series is ungrouped and spans the full category width as a background reference.
- The "Europe" and "Asia" series are grouped by default, sharing their portion of the category width.
- When a series has `grouped: false`, its `widthRatio` is relative to the full category width.
- When `grouped: true` (the default), `widthRatio` is relative to the automatically calculated width allocated to each series within group.

## Skip Null Bars

Use the `skipNullBars` option on a [Category](https://www.ag-grid.com/charts/javascript/axes-types/#category), [Unit Time](https://www.ag-grid.com/charts/javascript/axes-time/#unit-time) or [Ordinal Time](https://www.ag-grid.com/charts/javascript/axes-time/#ordinal-time) axis to prevent bars with `null`, `undefined` or missing values from taking up space within each category band. This also closes the gap when a series supplies its own `data` array and a category is absent from it.

#### Skip Null Bars

```ts
import {
  AgCartesianChartOptions,
  AgCategoryAxisOptions,
  AgCharts,
  BarSeriesModule,
  CategoryAxisModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";
import { getData } from "./data";

ModuleRegistry.registerModules([
  BarSeriesModule,
  CategoryAxisModule,
  LegendModule,
  NumberAxisModule,
]);

const options: AgCartesianChartOptions = {
  data: getData(),
  title: {
    text: "Quarterly Revenue",
  },
  series: [
    {
      type: "bar",
      xKey: "quarter",
      yKey: "software",
      yName: "Software",
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "hardware",
      yName: "Hardware",
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "services",
      yName: "Services",
    },
    {
      type: "bar",
      xKey: "quarter",
      yKey: "investments",
      yName: "Investments",
    },
  ],
  axes: {
    x: {
      type: "category",
      skipNullBars: true,
    },
    y: {
      type: "number",
      label: {
        formatter: ({ value }) => `$${(value / 1000).toFixed(1)}B`,
      },
    },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);

function skipNullBars() {
  (options.axes!.x as AgCategoryAxisOptions).skipNullBars = true;

  chart.update(options);
}

function showNullBars() {
  (options.axes!.x as AgCategoryAxisOptions).skipNullBars = false;

  chart.update(options);
}

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).skipNullBars = skipNullBars;
  (<any>window).showNullBars = showNullBars;
}
```

[Live example: Skip Null Bars](https://www.ag-grid.com/charts/typescript/bars/examples/skip-null-bars)

```js
{
    axes: {
        x: {
            type: 'category',
            skipNullBars: true,
        },
    },
}
```

In this example:

- Various values in the data are set to `null`, `undefined` or missing.
- When an axis has `skipNullBars: true`, bars with `null`, `undefined` or missing values are not represented on the chart.
- Toggle between "Skip Null Bars" and "Show Null Bars" to compare the difference.

## API Reference

#### Bar Options

These properties are common to [Bar](https://www.ag-grid.com/charts/javascript/bar-series/), [Range Bar](https://www.ag-grid.com/charts/javascript/range-bar-series/), [Waterfall](https://www.ag-grid.com/charts/javascript/waterfall-series/) and [Box Plot](https://www.ag-grid.com/charts/javascript/box-plot-series/) series types.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| width | PixelSize |  | Fixed width of each bar in the series. |
| widthRatio | Ratio |  | Ratio of the bandwidth (or specified width) to use for the width for each bar in the series. |
| cornerRadius | PixelSize |  | Apply rounded corners to each bar. |
| fill | CssColor \| AgColorRef \| AgColorRefMixOnto \| AgColorRefMixOntoColor \| AgGradientColor \| AgPatternColor \| AgImageFill |  | The colour for filling shapes. A colour string, or an object for a gradient, pattern, or image fill. |
| fillOpacity | Opacity |  | The opacity of the fill colour. |
| stroke | CssColor \| AgColorRef \| AgColorRefMixOnto \| AgColorRefMixOntoColor |  | The colour for the stroke. |
| strokeWidth | PixelSize |  | The width of the stroke in pixels. |
| strokeOpacity | Opacity |  | The opacity of the stroke colour. |
| lineDash | PixelSize[] |  | An array specifying the length in pixels of alternating dashes and gaps. |
| lineDashOffset | PixelSize |  | The initial offset of the dashed line in pixels. |

#### Band Alignment

This property is available on [Category](https://www.ag-grid.com/charts/javascript/axes-types/#category), [Ordinal Time](https://www.ag-grid.com/charts/javascript/axes-time/#ordinal-time) and [Unit Time](https://www.ag-grid.com/charts/javascript/axes-time/#unit-time) axes.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| bandAlignment | 'justify' \| 'start' \| 'center' \| 'end' | 'justify' | The alignment of bands when used with bar-like series with fixed widths. |

#### Skip Null Bars

This property is available on [Category](https://www.ag-grid.com/charts/javascript/axes-types/#category), [Ordinal Time](https://www.ag-grid.com/charts/javascript/axes-time/#ordinal-time) and [Unit Time](https://www.ag-grid.com/charts/javascript/axes-time/#unit-time) axes.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| skipNullBars | boolean | false | Set to `true` to prevent bars with `null`, `undefined` or missing values from taking up space in each category. |
