Compose complex, data-driven visualizations from reusable charts and components with d3.

  • Get started quickly with standard charts and components
  • Layout charts and components automatically
  • Powerful foundation for creating custom charts and components

Composable

Create small and sharp charts/components that do one thing one well (e.g. Bars, Lines, Legend, Axis, etc.) and compose them to create complex visualizations.

d3.compose works great with your existing charts (even those from other libraries) and it is simple to extend/customize the built-in charts and components.

Automatic Layout

When creating complex charts with D3.js and d3.chart, laying out and sizing parts of the chart are often manual processes. With d3.compose, this process is automatic:

  • Automatically size and position components
  • Layer components and charts by z-index
  • Responsive by default, with automatic scaling

Why d3.compose?

  • Customizable: d3.compose makes it easy to extend, layout, and refine charts/components
  • Reusable: By breaking down visualizations into focused charts and components, you can quickly reconfigure and reuse your code
  • Integrated: It's straightforward to use your existing charts or charts from other libraries with d3.compose to create just the chart you're looking for

Getting Started

We'll walk through the steps for creating this chart plotting two series, A and B, for an experiment in the following steps. We'll start with a simple line chart and progressively add components and then switch to bars at the end (to see how easy that is).

1. Download

Download and add d3.compose (and its dependencies: D3.js and d3.chart) to your page.

<!doctype html>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="d3.compose.css">
  </head>
  <body>
    <div id="chart"></div>

    <script src="d3.js"></script>
    <script src="d3.chart.js"></script>

    <script src="d3.compose-all.js"></script>

    <script type="text/javascript">
      <!-- Your code -->
    </script>
  </body>
</html>

2. Add Chart

First, we'll take just the B results and add a line chart to visualize the data.

var data = [{x: 0, y: 25}, {x: 10, y: 52}, {x: 20, y: 73}, /* ... */];
var chart = d3.select('#chart').chart('Compose', function(data) {
  // Options function:
  // Gets evaluated on each draw and takes in data and returns options

  var charts = [
    // Create results chart:
    // d3c.lines -> type: 'Lines' -> d3.chart('Lines')
    // 'results' -> chart key for transitions and legend
    // {data: data} -> pass given data to Lines chart
    d3c.lines('results', {data: data})
  ];

  return [
    // Add charts to chart layer (d3c.layered)
    d3c.layered(charts)
  ];
});

chart.draw(data);

3. Add y-axis Component

The chart isn't very meaningful without knowing what the values are, so let's add a y-axis to the chart.

var chart = d3.select('#chart').chart('Compose', function(data) {
  var charts = [
    d3c.lines('results', {data: options.data})
  ];

  // Create yAxis component:
  // d3c.axis -> type: 'Axis' -> d3.chart('Axis')
  // 'yAxis' -> component key for applying transitions
  // {scale: ...} -> pass options to Axis
  var yAxis = d3c.axis('yAxis', {
    scale: {domain: [0, 100]}
  });

  // Return layout with rows/columns
  return [
    // Row with two columns
    // (d3c.layered expands to fill remaining row width)
    [yAxis, d3c.layered(charts)]
  ];
});

4. Add Scales

In the previous step, the y-axis domain coincidentally matched the line chart's scale, but if the line chart's scale needs to be changed (e.g. to given more room at the top of the chart), a scale can be created that is shared between components and charts. Additionally, scales can be created with the domain automatically set from the given data. Next, let's add an x-axis and change the scale for the y-axis.

var chart = d3.select('#chart').chart('Compose', function(data) {
  // Scales are created as simple objects (that are later converted into d3.scale)
  var scales = {
    x: {data: options.data, key: 'x'},
    y: {domain: [0, 120]}
  };

  var charts = [
    // Pass xScale and yScale options to Lines
    d3c.lines('results', {
      data: options.data,
      xScale: scales.x,
      yScale: scales.y
    })
  ];

  // Pass scales to respective axes
  var xAxis = d3c.axis('xAxis', {scale: scales.x});
  var yAxis = d3c.axis('yAxis', {scale: scales.y});

  return [
    [yAxis, d3c.layered(charts)],

    // Full-width row aligned with d3c.layered
    xAxis
  ];
});

5. Add Series

With the chart taking shape, let's add another data series: the A results. All of the standard charts handle both single or series data by default and there are helpers and mixins to add series functionality to your custom charts/components.

var data = [
  {
    // required: values: [...]
    // optional: key, name
    key: 'a',
    name: 'A', // (used in legend later)
    values: [{x: 0, y: 25}, {x: 10, y: 52}, {x: 20, y: 73}, /* ... */]
  },
  {
    key: 'b',
    name: 'B',
    values: [/* ... */]
  }
];
var chart = d3.select('#chart').chart('Compose', function(data) {
  // Everything stays the same...
});

chart.draw(data);

6. Add Legend

Now that there are multiple series, let's add a legend. The legend component can take in a data property to manually create values, or it can automatically load series information from each chart passed to charts (by key).

var chart = d3.select('#chart').chart('Compose', function(data) {
  var scales = {
    x: {data: options.data, key: 'x'},
    y: {domain: [0, 120]}
  };

  var charts = [
    d3c.lines('results', {
      data: options.data,
      xScale: scales.x,
      yScale: scales.y
    })
  ];

  var xAxis = d3c.axis('xAxis', {scale: scales.x});
  var yAxis = d3c.axis('yAxis', {scale: scales.y});

  // Create legend component
  // d3c.legend -> type: 'Legend' -> d3.chart('Legend')
  // charts: ['results'] -> generate legend from the 'results' chart
  var legend = d3c.legend({charts: ['results']});

  return [
    // Place legend in column to the right of charts
    [yAxis, d3c.layered(charts), legend],
    xAxis
  ];
});

7. Add Titles

Finally, let's add titles to the chart and axes.

var chart = d3.select('#chart').chart('Compose', function(data) {
  var scales = {
    x: {data: options.data, key: 'x'},
    y: {domain: [0, 120]}
  };

  var charts = [
    d3c.lines('results', {
      data: options.data,
      xScale: scales.x,
      yScale: scales.y
    })
  ];

  var xAxis = d3c.axis('xAxis', {scale: scales.x});
  var yAxis = d3c.axis('yAxis', {scale: scales.y});
  var legend = d3c.legend({charts: ['results']});
  var title = d3c.title('d3.compose');
  var xAxisTitle = d3c.axisTitle('Input');
  var yAxisTitle = d3c.axisTitle('Results');

  return [
    title,
    [yAxisTitle, yAxis, d3c.layered(charts), legend],
    xAxis,
    xAxisTitle
  ];
});

8. Switch to Bars

Your colleagues decide they want to see what the chart would look like with bars instead of lines (a potentially major change without d3.compose). Just two things need to change: switch 'Lines' to 'Bars' and the x-scale needs to use an ordinal scale.

var chart = d3.select('#chart').chart('Compose', function(data) {
  var scales = {
    // Use d3.scale.ordinal(...)
    // and place series next to each other (adjacent)
    x: {type: 'ordinal', data: options.data, key: 'x', adjacent: true},
    // y: ...
  };

  var charts = [
    d3c.bars('results', {/* ... */})
  ];

  // Everything else stays the same...
});