Skip to content

How can Plottable be extended to support modern ESM (ECMAScript Modules) and tree-shaking in bundlers like Vite or Webpack 5 without rewriting the core architecture? #3617

@Montana

Description

@Montana

Hi all,

From my understanding Plottable was originally built around UMD/CommonJS patterns. What would be required to introduce first-class ESM support (with proper sideEffects flags and module exports) to enable tree-shaking in modern bundlers like Vite and Webpack 5? Would this require a major refactor of the internal architecture, or could it be introduced incrementally?

The Structural Limitation Today

At present, consumers typically import Plottable as a single namespace:

import * as Plottable from "plottable";

This pattern effectively forces bundlers to include the entire exported surface area of the library. Even if a developer only uses a single plot type (for example, Line), the bundler has no reliable way to eliminate unused plot types, scales, interactions, components, and utilities.

Because the package is distributed as a monolithic UMD/CJS bundle, static analysis becomes difficult. Tree-shaking relies on static import graphs. UMD patterns and namespace aggregation obscure that graph.

So I believe modern libraries expose multiple entry points and granular exports. Instead of importing the entire namespace, consumers can import only what they need:

import { Line } from "plottable/plots";
import { Time } from "plottable/scales";
import { Axis } from "plottable/axes";

Incremental Migration Path (If a Full Rewrite Is Risky)

A complete architectural rewrite may not be necessary. If one is needed though, it may look like this, convert internal TypeScript modules to ESM syntax while preserving structure, and introducing a parallel ESM build using Rollup or ESBuild.

Maintain dual outputs:
*plottable.cjs.js
*plottable.esm.js

Lastly gradually expose granular export paths. So here's an example rollup config:

export default {
  input: "src/index.ts",
  output: [
    {
      file: "dist/plottable.esm.js",
      format: "es"
    },
    {
      file: "dist/plottable.cjs.js",
      format: "cjs"
    }
  ],
  external: ["d3"]
};

This allows backward compatibility while progressively enabling modern optimization.


Why This Matters Strategically

In 2026, the viability of a JavaScript library is no longer determined solely by whether it “works.” It increasingly depends on how well it aligns with modern ecosystem expectations. Bundle efficiency, native ESM compatibility, smooth framework integration, and TypeScript-first workflows are now baseline requirements rather than optional enhancements. Even if Plottable remains technically functional and stable, the absence of proper ESM support can create perception challenges.

Developers evaluating charting libraries today often assess factors like tree-shakability, bundle size impact, lazy-loading compatibility, and integration friction with React or Next.js before they even evaluate feature depth. Without modern packaging, Plottable risks being excluded early in technical evaluations — not because of deficiencies in charting capability, but because of ecosystem misalignment.


Performance Implications

The performance implications are practical rather than theoretical. Consider a typical import pattern:

import { Line } from "plottable";

Under the current packaging model, a bundler may end up including most of the library, even if only the Line plot is used. Static analysis becomes less effective when the export surface is aggregated and not structured for granular elimination.

With proper ESM support and accurate sideEffects configuration, developers could instead write:

import { Line } from "plottable/plots";

In that scenario, only the Line plot and its direct dependencies would be bundled. In real-world dashboards — particularly in enterprise environments where performance budgets matter, this could meaningfully reduce initial JavaScript payload size. Over time, such efficiencies compound across applications and deployment surfaces.


Developer Experience and Modern Patterns

Native ESM support also unlocks more ergonomic development patterns. For example, code splitting and lazy loading become straightforward when modules are exposed granularly:

const LinePlot = React.lazy(() =>
  import("plottable/plots/line")
);

Without modular export paths, this pattern is difficult or impractical to implement. Beyond performance, this impacts architectural flexibility in modern React and Next.js applications.

These are just some thoughts,
Michael

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions