luvv to helpDiscover the Best Free Online Tools
Topic 7 of 8

Writing Reusable Components

Learn Writing Reusable Components for free with explanations, exercises, and a quick test (for Data Visualization Engineer).

Published: December 28, 2025 | Updated: December 28, 2025

Why this matters

As a Data Visualization Engineer, you often need to produce multiple charts with consistent behavior, styling, and performance. Reusable components help you:

  • Ship dashboards faster by composing well-tested pieces.
  • Keep a consistent look and feel (branding, accessibility, tooltips).
  • Reduce bugs via single sources of truth for shared logic (scales, legends).
  • Empower teammates to use your work with simple parameters or props.
Real tasks you will face
  • Turn a one-off Matplotlib chart into a company-standard function with theme support.
  • Wrap a D3 chart into a reusable factory with getters/setters for width, height, and data accessors.
  • Create a React chart component that handles empty states, loading, and color palettes.

Concept explained simply

A reusable component is a small, self-contained building block that takes inputs, applies predictable logic, and produces a consistent output.

Mental model

  • Inputs: data + config (props/parameters).
  • Processor: pure logic that doesn’t mutate external state.
  • Output: a figure, axes, SVG, or DOM node.
  • Contract: documented inputs and defaults; stable interface over time.
Key principles
  • Single responsibility: one chart = one idea (e.g., line chart with optional trendline).
  • Configurable via parameters/props with sensible defaults.
  • Pure when possible: return new objects; don’t silently mutate global state.
  • Separation of concerns: data accessors vs. styling vs. interaction.
  • Accessible by default: colorblind-friendly palettes, ARIA labels or descriptive titles.

Design principles for reusable visualization components

  • Stable API: name parameters/props clearly; avoid sudden breaking changes.
  • Default theme: fonts, colors, grids; override via optional config.
  • Data accessors: pass functions like x(d) and y(d) in JS, or column names in Python.
  • Error handling: friendly messages for empty data or wrong types.
  • Extensibility: optional hooks (e.g., onHover, formatter) and slots (e.g., custom tooltip).
  • Testability: deterministic output given the same inputs.

Worked examples

Example 1 — Python (Matplotlib) reusable bar chart

import matplotlib.pyplot as plt
from typing import List, Optional, Dict

DEFAULT_COLORS = ["#4E79A7", "#F28E2B", "#E15759", "#76B7B2", "#59A14F"]

def bar_chart(ax, categories: List[str], values: List[float], *,
              title: str = "", color: Optional[str] = None,
              palette: Optional[List[str]] = None,
              value_labels: bool = True,
              grid: bool = True,
              fmt: Dict = None):
    """
    Draw a bar chart on the provided Axes and return it.
    - categories, values: aligned lists
    - color: single color for all bars
    - palette: per-bar colors (overrides color)
    - value_labels: show numeric labels
    - grid: toggle y-grid lines
    - fmt: dict of extra Matplotlib kwargs (e.g., alpha)
    """
    if fmt is None: fmt = {}
    assert len(categories) == len(values), "categories and values must align"

    if palette is None and color is None:
        palette = DEFAULT_COLORS * ((len(values) // len(DEFAULT_COLORS)) + 1)

    bar_colors = palette[:len(values)] if palette else color
    bars = ax.bar(categories, values, color=bar_colors, **fmt)

    if grid:
        ax.yaxis.grid(True, linestyle='--', alpha=0.3)

    if title:
        ax.set_title(title)

    if value_labels:
        for rect, v in zip(bars, values):
            ax.text(rect.get_x() + rect.get_width()/2, v, f"{v}",
                    ha='center', va='bottom', fontsize=9)

    ax.set_ylabel("Value")
    return ax

# Usage
fig, ax = plt.subplots(figsize=(6,4))
bar_chart(ax, ["A","B","C"], [5,3,7], title="Sales by Segment")
plt.tight_layout()
plt.show()
Why this is reusable
  • Accepts any categories/values with basic validation.
  • Theme through defaults (palette, grid, labels).
  • Extensible via fmt kwargs.

Example 2 — JavaScript (D3) reusable chart factory

// D3 v6+ style chart factory
function barChart() {
  let width = 600, height = 300, margin = {top: 24, right: 16, bottom: 40, left: 48};
  let xAccessor = d => d.category, yAccessor = d => d.value;
  let color = "#4E79A7", title = "";

  function chart(selection) {
    selection.each(function(data) {
      const innerW = width - margin.left - margin.right;
      const innerH = height - margin.top - margin.bottom;

      const x = d3.scaleBand()
        .domain(data.map(xAccessor))
        .range([0, innerW])
        .padding(0.2);

      const y = d3.scaleLinear()
        .domain([0, d3.max(data, yAccessor)])
        .nice()
        .range([innerH, 0]);

      const svg = d3.select(this)
        .selectAll("svg")
        .data([null])
        .join("svg")
          .attr("width", width)
          .attr("height", height);

      const g = svg.selectAll("g.chart")
        .data([null])
        .join("g")
          .attr("class", "chart")
          .attr("transform", `translate(${margin.left},${margin.top})`);

      g.selectAll("rect.bar")
        .data(data)
        .join("rect")
          .attr("class", "bar")
          .attr("x", d => x(xAccessor(d)))
          .attr("y", d => y(yAccessor(d)))
          .attr("width", x.bandwidth())
          .attr("height", d => innerH - y(yAccessor(d)))
          .attr("fill", color);

      g.selectAll(".x-axis")
        .data([null])
        .join(el => el.append("g").attr("class", "x-axis"))
          .attr("transform", `translate(0,${innerH})`)
          .call(d3.axisBottom(x));

      g.selectAll(".y-axis")
        .data([null])
        .join(el => el.append("g").attr("class", "y-axis"))
          .call(d3.axisLeft(y).ticks(5));

      if (title) {
        svg.selectAll("text.title")
          .data([title])
          .join("text")
            .attr("class", "title")
            .attr("x", margin.left)
            .attr("y", 16)
            .attr("font-weight", 600)
            .text(title);
      }
    });
  }

  // Getters/Setters
  chart.width = function(x) { if (!arguments.length) return width; width = x; return chart; };
  chart.height = function(x) { if (!arguments.length) return height; height = x; return chart; };
  chart.margin = function(x) { if (!arguments.length) return margin; margin = x; return chart; };
  chart.xAccessor = function(x) { if (!arguments.length) return xAccessor; xAccessor = x; return chart; };
  chart.yAccessor = function(x) { if (!arguments.length) return yAccessor; yAccessor = x; return chart; };
  chart.color = function(x) { if (!arguments.length) return color; color = x; return chart; };
  chart.title = function(x) { if (!arguments.length) return title; title = x; return chart; };

  return chart;
}

// Usage
// d3.select("#root").datum(data).call(barChart().title("Sales by Segment"));
Why this is reusable
  • Encapsulates state with closures and exposes setters.
  • Works with any selection via selection.call(chart).
  • Stable contract: data array + accessors.

Example 3 — React functional LineChart with props

import React from "react";

export function LineChart({
  data = [],
  xKey = "x",
  yKey = "y",
  width = 600,
  height = 300,
  color = "#4E79A7",
  emptyText = "No data"
}) {
  const margin = { top: 20, right: 20, bottom: 30, left: 40 };
  const innerW = width - margin.left - margin.right;
  const innerH = height - margin.top - margin.bottom;

  if (!data.length) {
    return 
{emptyText}
; } const xs = data.map(d => d[xKey]); const ys = data.map(d => d[yKey]); const minX = Math.min(...xs), maxX = Math.max(...xs); const minY = Math.min(...ys), maxY = Math.max(...ys); const xScale = (v) => (v - minX) / (maxX - minX || 1) * innerW; const yScale = (v) => innerH - (v - minY) / (maxY - minY || 1) * innerH; const pathD = data.map((d, i) => { const x = xScale(d[xKey]); const y = yScale(d[yKey]); return (i === 0 ? `M ${x},${y}` : `L ${x},${y}`); }).join(" "); return ( ); }
Why this is reusable
  • All behavior through props; sensible defaults.
  • Accessible output with roles and labels.
  • Works across datasets with xKey/yKey.

Steps to build a reusable component

  1. Define the contract: required inputs, optional config, defaults, and return value.
  2. Isolate logic: compute scales/layout in pure functions; keep rendering thin.
  3. Handle edge cases: empty data, NaNs, single-point series, long labels.
  4. Add theme hooks: colors, fonts, spacing via parameters/props.
  5. Document: one usage example + parameter table in docstring or component JSDoc.
  6. Test: small unit tests for formatters, accessors; visual spot-check with 2–3 datasets.

Exercises you can do now

These match the tasks in the Exercises section below. Try them here, then compare with solutions.

Exercise 1 (Python): Refactor a one-off bar chart into a function

  • Create bar_chart(ax, categories, values, title="", color=None, palette=None) that returns the Axes.
  • Default to a color palette when none is provided.
  • Show value labels above bars and add a faint grid.

Exercise 2 (JavaScript): Convert a D3 snippet into a chart factory

  • Expose width, height, margin, xAccessor, yAccessor, color, and title via getters/setters.
  • Ensure repeated calls update, not recreate, axes.
Checklist — Definition of Done
  • Inputs validated and documented.
  • Defaults produce a good-looking chart.
  • No hidden global state; returns a handle (Axes/selection/component) for further composition.
  • Graceful empty/invalid data handling.
  • At least one test dataset used to verify behavior.

Common mistakes and self-check

  • Overfitting to one dataset: If labels overlap or fail with longer text, it’s not reusable. Self-check: test with 2x longer category names.
  • Hidden side effects: Mutating global Matplotlib rcParams or shared scales. Self-check: run component twice and compare outputs.
  • Inflexible API: Hardcoded colors/axes. Self-check: can you change color or margins without editing internals?
  • No empty-state handling: Crashes on empty arrays. Self-check: pass an empty list and observe behavior.
  • Untested formatters: Number/date formatting breaking on locales. Self-check: unit-test formatters with edge values.

Practical projects

  • Reusable KPI card: a small component that displays a metric, delta, and sparkline in both Python (Matplotlib) and JS (SVG).
  • Company theme module: centralize palettes, font sizes, and spacing, then inject into your chart components.
  • Timeseries dashboard: line chart, movable average overlay, and hover tooltip, each as separate pluggable components.

Who this is for

  • Data Visualization Engineers who want consistent, scalable charts in Python or JS.
  • Analysts/Engineers moving from ad-hoc plots to component libraries.

Prerequisites

  • Basic Python (functions, lists) and Matplotlib/Seaborn familiarity, or
  • Basic JavaScript (ES6), DOM, and either D3 or React fundamentals.

Learning path

  1. Start with the worked examples above; retype them to understand the flow.
  2. Complete Exercises 1–2 in your environment.
  3. Build one practical project (KPI card or theme module).
  4. Take the quick test to verify your understanding.

Quick test note

The quick test is available to everyone. Only logged-in users get their progress saved.

Next steps

  • Abstract repeated styles into a theme config.
  • Add typing (Python type hints or TypeScript) for stronger contracts.
  • Write a short README or docstring with a before/after example.

Mini challenge

Pick one of your past charts. In 30 minutes, wrap it into a reusable component with defaults, validation, and at least two optional props/parameters. Test it on a different dataset.

Practice Exercises

2 exercises to complete

Instructions

Refactor a one-off bar plot into a reusable function:

  1. Create bar_chart(ax, categories, values, title="", color=None, palette=None, value_labels=True, grid=True, fmt=None).
  2. Validate lengths and provide a default color palette if neither color nor palette is passed.
  3. Add value labels and a faint y-grid. Return the Axes.
  4. Test with two datasets: short categories and long category names.
# Starting snippet
import matplotlib.pyplot as plt
cats = ["A", "B", "C"]
vals = [5, 3, 7]
fig, ax = plt.subplots()
ax.bar(cats, vals)
plt.show()
Expected Output
A function that, when called with different datasets and optional styling, consistently renders labeled bars with a grid and returns the Axes.

Writing Reusable Components — Quick Test

Test your knowledge with 8 questions. Pass with 70% or higher.

8 questions70% to pass

Have questions about Writing Reusable Components?

AI Assistant

Ask questions about this tool