Featured in Svelte Design Patterns (2022)
A bar chart race is a horizontal bar chart that animates bars over an interval (usually time). The x-axis represents the bar chart values. The bars are sorted based on their value from highest to lowest as the interval changes.
This library exports the following components:
# NPM
npm i svelte-bar-chart-race
# pnpm
pnpm i svelte-bar-chart-race
# Yarn
yarn add svelte-bar-chart-race
# Bun
bun i svelte-bar-chart-race
The examples use data sampled from StackOverflow's annual developer survey for "Most Loved Languages."
// demo/data.ts
export const data = [
{
title: "Rust",
color: "#dea584",
values: [
{ year: 2021, value: 86.98 },
{ year: 2020, value: 86.1 },
{ year: 2019, value: 83.5 },
{ year: 2018, value: 78.9 },
{ year: 2017, value: 73.1 },
],
},
{
title: "TypeScript",
color: "#2b7489",
values: [
{ year: 2021, value: 72.73 },
{ year: 2020, value: 67.1 },
{ year: 2019, value: 73.1 },
{ year: 2018, value: 67.0 },
{ year: 2017, value: 64.1 },
],
},
{
title: "Python",
color: "#3572A5",
values: [
{ year: 2021, value: 67.83 },
{ year: 2020, value: 66.7 },
{ year: 2019, value: 73.1 },
{ year: 2018, value: 68.0 },
{ year: 2017, value: 62.7 },
],
},
{
title: "Go",
color: "#00ADD8",
values: [
{ year: 2021, value: 62.74 },
{ year: 2020, value: 62.3 },
{ year: 2019, value: 67.9 },
{ year: 2018, value: 65.6 },
{ year: 2017, value: 63.3 },
],
},
{
title: "Swift",
color: "#F05138",
values: [
{ year: 2021, value: 63.56 },
{ year: 2020, value: 59.5 },
{ year: 2019, value: 69.2 },
{ year: 2018, value: 65.1 },
{ year: 2017, value: 63.7 },
],
},
];
In the simplest example, all you need is to pass data to BarChartRace.
Then, pass Chart and Slider through its default slot.
<script>
import { BarChartRace, Slider, Chart } from "svelte-bar-chart-race";
import { data } from "./data";
</script>
<BarChartRace {data}>
<Chart />
<Slider />
</BarChartRace>
Source: demo/Simple.svelte
The currentValue prop supports two-way binding.
In this example, the initial value is 2018. Any change to the currentValue via Slider will be reflected to the bound value.
currentValue can also be programmatically updated.
Finally, setting currentValue to null resets the value to the lowest value (in this case, 2017).
<script>
import { BarChartRace, Slider, Chart } from "svelte-bar-chart-race";
import { data } from "./data";
let currentValue = 2018;
</script>
<BarChartRace {data} bind:currentValue>
<Slider />
<Chart />
</BarChartRace>
<button on:click={() => (currentValue = 2020)}> Set value to 2020 </button>
<button on:click={() => (currentValue = null)}> Set value to null </button>
Current value:
<strong>{currentValue}</strong>
Source: demo/TwoWayBinding.svelte
The kitchen sink showcases all available features.
An alternative to accessing the currentValue is to destructure it from the BarChartRace slot using the let: directive.
Other slot props include:
() => void): sets the current value to the maximum value defined in data() => void): sets the current value to the minimum value defined in data(value: number) => void): sets the current value to the provided value() => void): function to animate the bar chart() => void): function to pause the animationboolean): true if the chart is currently animating<script>
import { BarChartRace, Slider, Chart } from "svelte-bar-chart-race";
import { quadOut } from "svelte/easing";
import { data } from "./data";
</script>
<BarChartRace
{data}
interval={1_500}
options={{ key: "year" }}
let:currentValue={value}
let:setMax
let:setMin
let:setValue
let:play
let:pause
let:isPlaying
>
<Slider labelText="Year: {value}" />
<Chart
--bar-height="10px"
--bar-margin-bottom="12px"
animate={{
easing: quadOut,
duration: 400,
delay: 50,
}}
let:datum
>
<strong>{datum.title}</strong>
{datum.value.toFixed(1)}%
</Chart>
<button on:click={isPlaying ? pause : play}>
{isPlaying ? "Pause" : "Play"}
</button>
<button on:click={setMax}>Set max</button>
<button on:click={setMin}>Set min</button>
<button on:click={() => setValue(2019)}> Set value to 2019 </button>
</BarChartRace>
Source: demo/KitchenSink.svelte
| Prop | Type | Default value |
|---|---|---|
| data | BarChartRaceDatum[] |
[] |
| options | BarChartRaceOptions |
{ key: "", unit: "%", max: 100 } |
| interval | number |
2_000 |
If no key is explicitly provided to options, BarChartRace will infer the key by finding the first key that is not "value" in data[0].values.
interface BarChartRaceDatum {
/**
* Title must be unique because it's
* used as a key in `Chart.svelte`.
*/
title: string;
/**
* Specify the chart bar color.
* @default "#333"
* @example "red"
* @example "#f00"
*/
color?: string;
/**
* Tabular data used as values for each bar.
* @example
* [
{ key: 2021, value: 63.56 },
{ key: 2020, value: 59.5 },
// ...
]
*/
values: BarChartRaceValue[];
}
interface BarChartRaceOptions {
/**
* Specify the key used to normalize values in the `data.values` array.
* If no key is specified, it will be inferred.
* @example
* For a value object { year: 2021, value: 62.74 },
* the inferred key would be "year".
*/
key?: any;
/**
* Specify the unit.
* @default "%"
*/
unit?: string;
/**
* Specify the maximum value the
* chart value should be divided by.
* @default 100
*/
max?: number;
}
Use the default slot to pass through the Chart and Slider components or any other element.
<BarChartRace
{data}
let:currentValue
let:isPlaying
let:setMax
let:setMin
let:setValue
let:play
let:pause
>
<!-- Default slot -->
</BarChartRace>
| Prop | Type | Default value |
|---|---|---|
| animate | FlipParams |
{ delay: 0, duration: d => Math.sqrt(d) * 120, easing: cubicOut } |
The Chart component uses the flip function to animate the bar chart.
Svelte provides numerous easing functions you can use to customize the animation.
<script>
import { quadOut } from "svelte/easing";
</script>
<Chart
animate={{
easing: quadOut,
duration: 400,
delay: 50,
}}
/>
Customize the chart styles using --style-props.
| Style prop | Description | Default value |
|---|---|---|
| --bar-height | Chart bar height | 6px |
| --bar-margin-top | Chart bar margin top | 4px |
| --bar-margin-bottom | Chart bar margin bottom | 10px |
| --chart-padding | ol element padding |
0 |
<Chart
--bar-height="10px"
--margin-top="20px"
--margin-bottom="20px"
--chart-padding="40px"
/>
Use the default slot to customize the bar chart display value.
Access arbitrary values from data values.
<Chart let:datum>
<strong>{datum.title}</strong>
{datum.value}%
{datum.additionalValue ?? ""}
</Chart>
| Name | Type | Default value |
|---|---|---|
| id | string |
"slider-" + Math.random().toString(36) |
| labelText | any |
"" |
The current value from BarChartRace will be used as the labelText.
Customize labelText to override this behavior.
<Slider labelText="Year: {value}" />