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

@ninesstudios/whiteboard

v0.2.2

Published

Whiteboard drawing application

Readme

Whiteboard

A tailored React Whiteboard component that supports drawing, shapes, text, images, and more. It comes with built-in state management and a customizable toolbar.

Features

  • Drawing Tools: Pencil, Text, Rectangle, Circle, Triangle, Diamond, Star, Heart, Hexagon, Octagon, Arrow.
  • Image Support: Insert and manipulate images on the canvas.
  • Canvas Controls: Pan and Zoom functionality.
  • Grid System: Toggleable background grid.
  • Selection: Select, move, and modify elements.
  • Export: Export the whiteboard content as a PNG image.
  • Locking Mechanism: Set the whiteboard to read-only mode with custom messages.
  • Action Handling: Hook into internal actions for external syncing or logging.

Installation

Install the package and its peer dependencies:

npm install whiteboard react react-dom

Note: If this package is not yet published to npm, you can install it locally or link it.

Usage

Basic Usage

Import the Whiteboard component and the stylesheet. The component takes up 100% of the parent container's width and height.

import React from 'react';
import Whiteboard from 'whiteboard';
import 'whiteboard/style.css'; // Import styles

function App() {
  return (
    <div style={{ width: '100vw', height: '100vh', border: '1px solid #ccc' }}>
      <Whiteboard />
    </div>
  );
}

export default App;

Advanced Usage

You can control the whiteboard state and handle events using props and a ref.

import React, { useRef } from 'react';
import Whiteboard from 'whiteboard';
import 'whiteboard/style.css';

function App() {
  const whiteboardRef = useRef(null);

  const handleAction = (action) => {
    console.log('Action occurred:', action);
    // Send action to server for real-time collaboration
  };

  const handleLockChange = (isLocked) => {
    console.log('Lock state changed:', isLocked);
  };

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <Whiteboard
        ref={whiteboardRef}
        onAction={handleAction}
        isLocked={false}
        lockText="This board is currently locked"
        onLockChange={handleLockChange}
      />
    </div>
  );
}

// Example: read & replace elements
// const elements = whiteboardRef.current?.getElements();
// whiteboardRef.current?.setElements(elements);

// Example: clear all elements
// whiteboardRef.current?.clearElements();

Programmatic Updates (applyAction)

You can programmatically apply actions to the whiteboard using the applyAction method exposed via the ref. This is essential for implementing real-time collaboration where you need to apply actions received from a server.

import React, { useEffect, useRef } from 'react';
import Whiteboard from 'whiteboard';

function App() {
  const whiteboardRef = useRef(null);

  useEffect(() => {
    // Example: mimicking receiving an action from a websocket
    const mockIncomingAction = {
      type: 'whiteboard/addElement',
      payload: {
        id: 'remote-1',
        type: 'rectangle',
        x: 200,
        y: 200,
        width: 100,
        height: 100,
        fillColor: '#FF0000'
      }
    };

    // Apply the action to the whiteboard
    if (whiteboardRef.current) {
        // In a real app, this would be inside a socket event listener
        setTimeout(() => {
            whiteboardRef.current.applyAction(mockIncomingAction);
        }, 1000);
    }
  }, []);

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <Whiteboard ref={whiteboardRef} />
    </div>
  );
}

API Reference

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | onAction | (action: object) => void | undefined | Callback fired for content-changing actions (e.g., add/remove/move elements, text edits). It does not emit high-frequency UI actions like pan/zoom/selection/tool changes. | | isLocked | boolean | false | When true, disables all editing tools and shows a lock indicator. | | lockText | string | undefined | Custom text to display on the lock indicator when isLocked is true. | | onLockChange | (isLocked: boolean) => void | undefined | Callback fired when the lock state changes. | | onImageUpload | (file: File) => Promise<string> | undefined | Async callback to handle image uploads. Must return a Promise that resolves to the image URL. |

Handling Image Uploads

By default, the whiteboard creates temporary Blob URLs for uploaded images. This works great for local sessions but won't work for real-time collaboration because Blob URLs are local to the browser tab.

To support multiplayer image sharing, you must provide the onImageUpload prop. This function should upload the file to your server (e.g., S3, Cloudinary) and return the public URL.

const handleImageUpload = async (file) => {
  // 1. Upload file to your server
  const formData = new FormData();
  formData.append('file', file);
  
  const response = await fetch('https://your-api.com/upload', {
    method: 'POST',
    body: formData
  });
  
  const data = await response.json();
  
  // 2. Return the public URL
  return data.url; 
};

<Whiteboard onImageUpload={handleImageUpload} />

Ref Methods

You can access these methods by passing a ref to the Whiteboard component.

| Method | Arguments | Description | |--------|-----------|-------------| | applyAction | (action: object) | Dispatches an action to the internal Redux store. Useful for applying remote updates. | | getElems / getElements | () | Returns the current elements array from the internal store. | | setElems / setElements | (elements: object[]) | Replaces the entire elements array in the internal store. | | clearElems / clearElements | () | Clears all elements from the internal store. | | canvas | - | Returns the underlying HTMLCanvasElement. |

Redux Actions Helper

For convenience (e.g., building your own applyAction payloads), the package exports the internal Redux action creators:

import { whiteboardActions } from 'whiteboard';

// Example: build an action object to send/apply
const action = whiteboardActions.clearElements();
// action.type === 'whiteboard/clearElements'

All available actions

| Action creator | action.type | Payload | |---|---|---| | toggleGrid() | whiteboard/toggleGrid | none | | setGridSize(size) | whiteboard/setGridSize | number | | setGridColor(color) | whiteboard/setGridColor | string | | setSelectedTool(tool) | whiteboard/setSelectedTool | string | | setToolProperty({ tool, property, value }) | whiteboard/setToolProperty | { tool: string, property: string, value: any } | | addElement(element) | whiteboard/addElement | Element (see “addElement payload” below) | | updateElement({ id, updates }) | whiteboard/updateElement | { id: string, updates: object } | | removeElement(id) | whiteboard/removeElement | string | | setSelectedElement(idOrNull) | whiteboard/setSelectedElement | string \| null | | clearElements() | whiteboard/clearElements | none | | setElements(elements) | whiteboard/setElements | object[] | | setPan({ x, y }) | whiteboard/setPan | { x: number, y: number } | | setZoom(zoom) | whiteboard/setZoom | number | | resetViewport() | whiteboard/resetViewport | none | | fitToView({ canvasWidth, canvasHeight, padding? }) | whiteboard/fitToView | { canvasWidth: number, canvasHeight: number, padding?: number } | | bringToFront(id) | whiteboard/bringToFront | string | | sendToBack(id) | whiteboard/sendToBack | string | | setLocked(isLocked) | whiteboard/setLocked | boolean | | setWatermarkEnabled(visible) | whiteboard/setWatermarkEnabled | boolean | | setWatermarkText(text) | whiteboard/setWatermarkText | string |

addElement(element) payload

addElement expects a full element object. Coordinates are world coordinates (the board handles pan/zoom internally).

Common fields (all element types):

| Field | Type | Notes | |---|---|---| | id | string | Must be unique (commonly Date.now().toString()). | | type | string | One of the supported element types below. |

Element schemas by type:

  • rectangle / triangle / diamond / hexagon / octagon

    • Required: x: number, y: number, width: number, height: number
    • Styling: strokeColor: string, fillColor: string, lineWidth: number
  • circle

    • Required: centerX: number, centerY: number, radius: number
    • Styling: strokeColor: string, fillColor: string, lineWidth: number
  • pencil

    • Required: points: Array<{ x: number, y: number }>
    • Styling: strokeColor: string, lineWidth: number
  • star / heart

    • Required: centerX: number, centerY: number, size: number
    • Styling: strokeColor: string, fillColor: string, lineWidth: number
  • arrow

    • Required: startX: number, startY: number, endX: number, endY: number
    • Styling: strokeColor: string, fillColor: string, lineWidth: number
  • text

    • Required: x: number, y: number, text: string
    • Styling: fontSize: number, fontColor: string
  • image

    • Required: x: number, y: number, width: number, height: number, src: string
    • Notes: src is typically a Data URL (from FileReader.readAsDataURL).

Example (rectangle):

import { whiteboardActions } from 'whiteboard';

const action = whiteboardActions.addElement({
  id: '1',
  type: 'rectangle',
  x: 100,
  y: 80,
  width: 200,
  height: 120,
  strokeColor: '#000000',
  fillColor: '#ffffff',
  lineWidth: 2,
});

License

MIT