Skip to content

⚡ SolidJS wrapper for Plotly.js – reactive and performant charts powered by Plotly, built for Solid.

License

Notifications You must be signed in to change notification settings

dsnchz/solid-plotly

Repository files navigation

solid-plotly

@dschz/solid-plotly

License npm Bundle Size JSR CI Discord

A modern, type-safe SolidJS component for Plotly.js charts with fine-grained reactivity and full event support.

✨ Features

  • 🏭 Factory Pattern - Plotly-agnostic design, bring your own Plotly.js bundle
  • SolidJS Reactivity - Fine-grained reactive updates for optimal performance
  • 📱 Responsive by Default - Automatic resizing with ResizeObserver
  • 🎯 Full TypeScript Support - Complete type safety with comprehensive JSDoc
  • 🎪 30+ Event Handlers - All official Plotly.js events supported

Contents

Installation

npm install solid-js @dschz/solid-plotly
# or
pnpm add solid-js @dschz/solid-plotly
# or
yarn add solid-js @dschz/solid-plotly
# or
bun add solid-js @dschz/solid-plotly

Plotly.js Distribution

Important: You must also install a Plotly.js distribution that suits your needs. This library is designed to be Plotly-agnostic, allowing you to choose the which PLotly distribution is optimal for the needs of your application.

For Web Applications (Recommended)

npm install plotly.js-dist-min

Other Available Distributions

# Full bundle (~3MB) - All chart types and features
npm install plotly.js-dist

# Basic bundle (~1MB) - Common chart types (scatter, bar, line, pie, etc.)
npm install plotly.js-basic-dist

# Minimal bundle (~400KB) - Essential charts only
npm install plotly.js-dist-min

# Custom build - Advanced users who want to cherry-pick modules
npm install plotly.js

Quick Start

Create a Plot component using the factory function with your chosen Plotly.js bundle:

import { createSignal } from "solid-js";
import Plotly from "plotly.js-dist-min";
import { createPlotComponent } from "@dschz/solid-plotly";

// Create the Plot component bound to your Plotly module
const Plot = createPlotComponent(Plotly);

function App() {
  return (
    <Plot
      data={[
        {
          x: [1, 2, 3],
          y: [2, 6, 3],
          type: "scatter",
          mode: "lines+markers",
          marker: { color: "red" },
        },
        { type: "bar", x: [1, 2, 3], y: [2, 5, 3] },
      ]}
      layout={{ title: "My First Chart" }}
      onClick={(event) => console.log("Clicked:", event.points)}
    />
  );
}

API Reference

Factory Function

createPlotComponent(Plotly: PlotlyModule)

Creates a Plot component bound to a specific Plotly.js module.

Parameters:

  • Plotly - Plotly.js module with required methods: newPlot, react, purge, Plots

Returns: SolidJS component for rendering Plotly charts

import Plotly from "plotly.js-basic-dist";
import { createPlotComponent } from "@dschz/solid-plotly";

const Plot = createPlotComponent(Plotly);

Component Props

Prop Type Default Description
data PlotlyData[] [] Array of data traces to plot (required)
layout PlotlyLayout {} Layout configuration for axes, title, margins, etc.
config PlotlyConfig {} Plotly configuration options for behavior and appearance
frames PlotlyFrame[] [] Animation frames for animated plots
useResize boolean true Enable automatic plot resizing when container size changes
id string "solid-plotly" Unique identifier for the plot container element
class string undefined CSS class name(s) to apply to the plot container
style JSX.CSSProperties { position: "relative", display: "inline-block" } Inline CSS styles for the plot container
onInitialized (figure, element) => void undefined Callback fired when the plot is first initialized
onUpdate (figure, element) => void undefined Callback fired when the plot data/layout is updated
onPurge (figure, element) => void undefined Callback fired when the plot is purged/destroyed
onResize () => void undefined Callback fired when the plot is resized
onError (error) => void undefined Callback fired when an error occurs during plot operations
ref (el: HTMLDivElement) => void undefined Ref callback to access the underlying HTML div element

Event Handlers

The component supports all official Plotly.js events through props. All event handlers are optional:

Mouse & Interaction Events

  • onClick - Fired when a data point is clicked
  • onDoubleClick - Fired when plot is double-clicked
  • onHover - Fired when hovering over a data point
  • onUnhover - Fired when mouse leaves a data point

Selection Events

  • onSelected - Fired when data points are selected
  • onSelecting - Fired continuously while selecting data points
  • onDeselect - Fired when selection is cleared

Layout Events

  • onRelayout - Fired when the plot layout is changed (zoom, pan, resize)
  • onRelayouting - Fired continuously while the layout is being changed
  • onRestyle - Fired when plot styling properties are changed

Legend Events

  • onLegendClick - Fired when a legend item is clicked
  • onLegendDoubleClick - Fired when a legend item is double-clicked

UI Element Events

  • onClickAnnotation - Fired when an annotation is clicked
  • onSliderChange - Fired when a slider value is changed
  • onSliderStart - Fired when slider interaction starts
  • onSliderEnd - Fired when slider interaction ends

Plot Lifecycle Events

  • onAfterPlot - Fired after plot rendering completes
  • onBeforePlot - Fired before plot rendering begins
  • onRedraw - Fired when plot is redrawn
  • onAutoSize - Fired when plot auto-resizes

Animation Events

  • onAnimated - Fired after animation completes
  • onAnimatingFrame - Fired when an animation frame is being processed
  • onAnimationInterrupted - Fired when animation is interrupted
  • onTransitioning - Fired during plot transitions
  • onTransitionInterrupted - Fired when transition is interrupted

Export Events

  • onAfterExport - Fired after export operation completes
  • onBeforeExport - Fired before export operation begins

Framework Events

  • onFramework - Fired for framework-specific events
  • onSunburstClick - Fired when a sunburst chart segment is clicked
  • onEvent - Generic event handler for any plot event

Examples

Reactive Data Updates

import { createSignal } from "solid-js";
import Plotly from "plotly.js-dist-min";
import { createPlotComponent, type PlotlyData } from "@dschz/solid-plotly";

const Plot = createPlotComponent(Plotly);

function ReactivePlot() {
  const [data, setData] = createSignal<PlotlyData[]>([
    { x: [1, 2, 3], y: [1, 4, 2], type: "scatter" },
  ]);

  const addPoint = () => {
    setData((prev) => [
      {
        ...prev[0],
        x: [...prev[0].x, prev[0].x.length + 1],
        y: [...prev[0].y, Math.random() * 10],
      },
    ]);
  };

  return (
    <div>
      <button onClick={addPoint}>Add Point</button>
      <Plot data={data()} layout={{ title: "Reactive Updates" }} />
    </div>
  );
}

Event Handling

import { createSignal } from "solid-js";
import type { PlotSelectionEvent } from "plotly.js";
import { createPlotComponent, type PlotlyData, type PlotlyLayout } from "@dschz/solid-plotly";

const Plot = createPlotComponent(Plotly);

function InteractivePlot() {
  const [selectedPoints, setSelectedPoints] = createSignal<PlotSelectionEvent["points"]>([]);
  const [data] = createSignal<PlotlyData[]>([
    { x: [1, 2, 3, 4], y: [10, 11, 12, 13], type: "scatter" },
  ]);
  const [layout] = createSignal<PlotlyLayout>({ title: "Click and Select Points" });

  return (
    <Plot
      data={data()}
      layout={layout()}
      onClick={(event) => {
        console.log("Clicked point:", event.points[0]);
      }}
      onSelected={(event) => {
        setSelectedPoints(event.points);
        console.log("Selected points:", event.points.length);
      }}
      onRelayout={(event) => {
        console.log("Layout changed:", event);
      }}
    />
  );
}

Responsive Layout

import { createSignal } from "solid-js";
import { createPlotComponent, type PlotlyData, type PlotlyLayout } from "@dschz/solid-plotly";

const Plot = createPlotComponent(Plotly);

function ResponsivePlot() {
  const [data] = createSignal<PlotlyData[]>([{ x: [1, 2, 3], y: [1, 4, 2], type: "scatter" }]);
  const [layout] = createSignal<PlotlyLayout>({ title: "Responsive Chart" });

  return (
    <div style={{ width: "100%", height: "400px" }}>
      <Plot
        data={data()}
        layout={layout()}
        useResize={true} // Default: true
        style={{ width: "100%", height: "100%" }}
      />
    </div>
  );
}

Animation

import { createSignal, createEffect, onCleanup } from "solid-js";
import { createPlotComponent, type PlotlyData, type PlotlyLayout } from "@dschz/solid-plotly";

const Plot = createPlotComponent(Plotly);

function AnimatedPlot() {
  const [frame, setFrame] = createSignal(0);
  const [isAnimating, setIsAnimating] = createSignal(false);
  const [layout] = createSignal<PlotlyLayout>({ title: "Animated Sine Wave" });

  createEffect(() => {
    if (!isAnimating()) return;

    const interval = setInterval(() => {
      setFrame((prev) => (prev + 1) % 60);
    }, 100);

    onCleanup(() => clearInterval(interval));
  });

  const data = (): PlotlyData[] => [
    {
      x: Array.from({ length: 50 }, (_, i) => i * 0.1),
      y: Array.from({ length: 50 }, (_, i) => Math.sin(i * 0.1 + frame() * 0.1)),
      type: "scatter",
      mode: "lines",
    },
  ];

  return (
    <div>
      <button onClick={() => setIsAnimating(!isAnimating())}>
        {isAnimating() ? "Stop" : "Start"} Animation
      </button>
      <Plot data={data()} layout={layout()} />
    </div>
  );
}

Customizing Plotly.js Bundle

Choose the Plotly.js bundle that best fits your needs:

// Full bundle (~3MB) - All chart types
import Plotly from "plotly.js-dist";
const Plot = createPlotComponent(Plotly);

// Basic bundle (~1MB) - Common chart types
import Plotly from "plotly.js-basic-dist";
const Plot = createPlotComponent(Plotly);

// Minimal bundle (~400KB) - Essential charts only
import Plotly from "plotly.js-dist-min";
const Plot = createPlotComponent(Plotly);

TypeScript Usage

The library provides comprehensive TypeScript support with utility types to reduce friction when defining your chart data and configurations:

import { createSignal } from "solid-js";
import {
  createPlotComponent,
  type PlotlyData,
  type PlotlyLayout,
  type PlotlyConfig,
  type PlotlyFigure,
  type PlotProps,
} from "@dschz/solid-plotly";

// Use utility types for your signals
const [data, setData] = createSignal<PlotlyData[]>([
  { x: [1, 2, 3], y: [1, 4, 2], type: "scatter" },
]);

const [layout, setLayout] = createSignal<PlotlyLayout>({
  title: "My Chart",
  xaxis: { title: "X Axis" },
  yaxis: { title: "Y Axis" },
});

const [config, setConfig] = createSignal<PlotlyConfig>({
  displayModeBar: true,
  responsive: true,
});

// Type-safe callbacks
const handleInitialized = (figure: PlotlyFigure, element: PlotlyHTMLElement) => {
  console.log("Initialized with", figure.data.length, "traces");
  setData(figure.data);
  setLayout(figure.layout);
};

// Create your Plot component
const Plot = createPlotComponent(Plotly);

function MyChart() {
  return (
    <Plot data={data()} layout={layout()} config={config()} onInitialized={handleInitialized} />
  );
}

Available Utility Types

  • PlotlyData - Single data trace type (use PlotlyData[] for the data prop)
  • PlotlyLayout - Layout configuration object
  • PlotlyConfig - Plotly configuration options
  • PlotlyFrame - Animation frame type (use PlotlyFrame[] for frames prop)
  • PlotlyFigure - Complete figure data structure (used in callbacks)
  • PlotProps - Complete props interface for the Plot component
  • PlotlyModule - Type for the Plotly.js module interface

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

For more examples and detailed documentation, visit the GitHub repository or check out the Discord community.

License

MIT License

About

⚡ SolidJS wrapper for Plotly.js – reactive and performant charts powered by Plotly, built for Solid.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages