D3 Svelte Bar Chart
D3 bar chart example, written in Svelte.
by Tony Dang on
This post is part of a series on converting the official D3 examples to Svelte.
D3 bar chart written in Svelte
Original Plot | Svelte REPL | Github
Code
<script>
import * as d3 from "d3";
// Receive plot data as prop.
export let data;
// The chart dimensions and margins as optional props.
export let width = 928;
export let height = 500;
export let marginTop = 30;
export let marginRight = 0;
export let marginBottom = 50;
export let marginLeft = 40;
// Create the x (horizontal position) scale.
const xScale = d3
.scaleBand()
.domain(
// Sort the data in descending frequency
d3.groupSort(
data,
([d]) => -d.frequency,
(d) => d.letter
)
)
.range([marginLeft, width - marginRight])
.padding(0.1);
// Create the y (vertical position) scale.
const yScale = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.frequency)])
.range([height - marginBottom, marginTop]);
</script>
<svg
{width}
{height}
viewBox="0 0 {width} {height}"
style:max-width="100%"
style:height="auto"
>
<!-- Add a rect for each bar. -->
<g fill="steelblue">
{#each data as d}
<rect
x={xScale(d.letter)}
y={yScale(d.frequency)}
height={yScale(0) - yScale(d.frequency)}
width={xScale.bandwidth()}
/>
{/each}
</g>
<!-- X-Axis -->
<g transform="translate(0,{height - marginBottom})">
<line stroke="currentColor" x1={marginLeft - 6} x2={width} />
{#each data as d}
<!-- X-Axis Ticks -->
<line
stroke="currentColor"
x1={xScale(d.letter) + xScale.bandwidth() / 2}
x2={xScale(d.letter) + xScale.bandwidth() / 2}
y1={0}
y2={6}
/>
<!-- X-Axis Tick Labels -->
<text
fill="currentColor"
text-anchor="middle"
x={xScale(d.letter) + xScale.bandwidth() / 2}
y={22}
>
{d.letter}
</text>
{/each}
<!-- X-Axis Label -->
<text fill="currentColor" x={width / 2} y={50}>letter</text>
</g>
<!-- Y-Axis -->
<g transform="translate({marginLeft},0)">
{#each yScale.ticks() as tick}
<!--
Y-Axis Ticks.
Note: First tick is skipped since the x-axis already acts as a tick.
-->
{#if tick !== 0}
<line
stroke="currentColor"
x1={0}
x2={-6}
y1={yScale(tick)}
y2={yScale(tick)}
/>
{/if}
<!-- Y-Axis Tick Labels -->
<text
fill="currentColor"
text-anchor="end"
dominant-baseline="middle"
x={-9}
y={yScale(tick)}
>
{Math.trunc(tick * 100)}
</text>
{/each}
<!-- Y-Axis Label -->
<text fill="currentColor" text-anchor="start" x={-marginLeft} y={15}>
↑ frequency (%)
</text>
</g>
</svg>
Things I learned
-
The official D3 Svelte example uses D3’s built-in axis generators combined with Svelte’s reactive statements for rendering axes. While this approach is concise, I found it less intuitive and it doesn’t allow for server-side rendering (SSR).
In my implementation, I chose not to use D3’s axis generators in order to have faster page load times and avoid unnecessary hydration. However, I can see cases where using D3’s axis generators would be a better choice.