npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

solid-infinite-canvas

v0.0.20

Published

Infinite canvas libray for SolidJS

Readme

solid-infinite-canvas

A powerful and flexible library for creating infinite, pannable, and zoomable canvases in SolidJS. Perfect for building diagrams, whiteboards, or any node-based editor. 🚀


Features

  • Infinite Canvas: Pan and zoom on a limitless 2D stage.
  • 📦 Custom Elements: Render any SolidJS component as a canvas element.
  • 🔌 Plugin System: Easily extend functionality with plugins for features like resizing, connections, etc.
  • ⚡️ Reactive State: A simple and powerful API to manage canvas and element state, built on SolidJS signals.
  • 🎨 Custom Backgrounds: Create dynamic, grid-based, or static backgrounds.
  • ⚫️ Multiple Stages: Render multiple independent canvases on the same page.
  • 🟦 Fully Typed: Written in TypeScript for a great developer experience.

Installation

# pnpm
pnpm add solid-infinite-canvas

# npm
npm install solid-infinite-canvas

# yarn
yarn add solid-infinite-canvas

Quick Start

Getting started is easy. Just create a stage context, define your element components, and render the <Stage>.

import { onMount } from 'solid-js'
import { createStageContext, Stage, CanvasElementComponent, useStage } from 'solid-infinite-canvas'

// 1. Define your custom element component
const RectangleElement: CanvasElementComponent = ({ element, elementId }) => {
  const { setState } = useStage()

  const changeColor = () => {
    const randomColor = `#${Math.floor(Math.random() * 16777215).toString(16)}`
    setState('elements', elementId, 'props', 'color', randomColor)
  }

  return (
    <div
      onClick={changeColor}
      style={{
        width: '100%',
        height: '100%',
        'background-color': element.props.color,
        border: '1px solid black',
        color: 'white',
        display: 'grid',
        'place-items': 'center',
        cursor: 'pointer',
      }}
    >
      Click Me!
    </div>
  )
}

// 2. Create a stage context
const stageContext = createStageContext()
const { actions } = stageContext

function App() {
  // 3. Add elements to the stage
  onMount(() => {
    actions.createElement({
      type: 'rectangle',
      rect: { x: 100, y: 100, width: 150, height: 100 },
      props: { color: 'cornflowerblue' },
    })
  })

  return (
    // 4. Render the Stage component (the Stage component fills its parent)
    <div style={{ width: '100vw', height: '100vh', position: 'relative' }}>
      <Stage
        context={stageContext}
        components={{
          elements: {
            rectangle: RectangleElement,
          },
        }}
      />
    </div>
  )
}

export default App

Core Concepts

The Stage Context

Everything in the canvas revolves around a stage context. You create one with createStageContext().

const stageContext = createStageContext()

This context object contains:

  • state: A SolidJS store holding the state for all elements, cursors, etc.
  • setState: The function to update the state.
  • camera: A signal with the current { x, y, zoom } of the camera.
  • actions: A set of helpful functions (createElement, centerContent, zoomIn, zoomOut) to control the stage.

You pass the whole context to the <Stage> component via the context prop.

Canvas Elements

Elements are just standard SolidJS components. For proper typing, use the CanvasElementComponent type. Inside your component, you can use the useStage() hook to access the stage context and update state.

import { CanvasElementComponent, useStage } from 'solid-infinite-canvas'

const MyElement: CanvasElementComponent = ({ element, elementId }) => {
  // Get access to the stage's setState function
  const { setState } = useStage()

  // Update this element's props when clicked
  const handleClick = () => {
    setState('elements', elementId, 'props', 'someValue', v => v + 1)
  }

  return <div onClick={handleClick}>{element.props.someValue}</div>
}

Plugins

Plugins are the primary way to add new features to the stage, like resizing elements or drawing connections between them.

Using Existing Plugins

The library ships with pre-built plugins. To use them, import the plugin and any components it provides, then add them to the <Stage> component.

For example, to add resizing and connection capabilities:

import ResizePlugin, { ElementTransformControls } from 'solid-infinite-canvas/plugins/ResizePlugin'
import ConnectionsPlugin, {
  ElementConnectionPoint,
} from 'solid-infinite-canvas/plugins/ConnectionsPlugin'

// In your Stage component:
const App = () => {
  return (
    <Stage
      context={stageContext}
      components={{
        elements: {
          /* your elements */
        },
      }}
      plugins={[ConnectionsPlugin, ResizePlugin]} // <-- Add plugins here
    />
  )
}

// In your element component:
const MyNode: CanvasElementComponent = ({ elementId }) => {
  return (
    <>
      <div class="my-node-body">...</div>

      {/* Add connection points from the ConnectionsPlugin */}
      <ElementConnectionPoint elementId={elementId} type="input" />
      <ElementConnectionPoint elementId={elementId} type="output" />

      {/* Add resize handles from the ResizePlugin */}
      <ElementTransformControls elementId={elementId} />
    </>
  )
}

Building Your Own Plugin

A plugin is an object with a name, and optional events and components properties.

  • name: A unique string identifier for the plugin.
  • events: An object of event handlers (onMouseDown, onWindowMouseMove, etc.). These handlers receive the event and the entire stage context, allowing you to react to user input and modify the stage state.
  • components: An object that can contain viewBack or viewFront components. These are rendered behind or in front of the main elements, respectively. Useful for things like connection lines (in the back) or tooltips (in the front).

Here is a simplified look at the ConnectionsPlugin structure:

import { StagePlugin } from 'solid-infinite-canvas'

const ConnectionsPlugin: StagePlugin = {
  name: 'connections',

  // React to mouse events to create connections
  events: {
    onMouseDown: (event, stage) => {
      const target = event.target as HTMLElement
      // Check if the user clicked on a connection point
      if (target.dataset.connectionPoint) {
        // Logic to start dragging a connection wire...
      }
    },
    onWindowMouseUp: (event, stage) => {
      // Logic to finalize the connection and update state...
      // Plugins can store their own state in stage.state.ext
      stage.setState('ext', 'connectionWires', newId, { from, to })
    },
  },

  // Render the connection wires behind the elements
  components: {
    viewBack: () => {
      const { state } = useStage()
      // Logic to get connection data from state.ext.connectionWires
      // and render SVG paths for each connection.
      return <svg>...</svg>
    },
  },
}

Creating Plugin Components

Plugin components like ElementConnectionPoint or ElementTransformControls are the interactive parts of a plugin that you place inside your elements.

The key is to use data-* attributes. The plugin's event handlers use these attributes to identify what the user is interacting with.

Here's the ElementConnectionPoint component. Notice how data-element-id and data-connection-point are used. The ConnectionsPlugin's onMouseDown event handler looks for these specific attributes on event.target to know that a connection drag has started.

import { Component } from 'solid-js'

export const ElementConnectionPoint: Component<{
  elementId: string
  type: 'input' | 'output'
}> = props => {
  return (
    <div
      // These attributes are how the plugin finds this component!
      data-element-id={props.elementId}
      data-connection-point={props.type}
      style={{
        position: 'absolute',
        width: '13px',
        height: '13px',
        background: 'orange',
        'border-radius': '50%',
        left: props.type === 'input' ? '0px' : '100%',
        top: '50%',
        transform: 'translate(-50%, -50%)',
        cursor: 'pointer',
      }}
    />
  )
}

License

This project is licensed under the MIT License. See the LICENSE file for details.