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 🙏

© 2025 – Pkg Stats / Ryan Hefner

use-transformable

v0.0.1

Published

A React library for creating transformable, draggable, resizable, and rotatable elements with multi-selection support

Readme

React Transformable

Alpha - Not prod ready

A React library for creating transformable, draggable, resizable, and rotatable elements with multi-selection support.

npm version License: MIT

Features

  • 🖱️ Drag & Drop: Smooth dragging with grid snapping support
  • 📏 Resize: Resize from any corner or edge with constraints
  • 🔄 Rotate: Rotate elements around custom origin points
  • Multi-Selection: Select and transform multiple elements at once
  • 🎨 Customizable: Theme support and custom styling
  • 📱 Touch Support: Works on mobile devices
  • 🔧 TypeScript: Full TypeScript support
  • Lightweight: No heavy dependencies

Installation

npm install react-transformable
# or
yarn add react-transformable

Basic Usage

import React, { useState } from 'react';
import { 
  TransformableProvider, 
  TransformableItem, 
  useTransformable,
  type Transformable 
} from 'react-transformable';

// Define your custom element type
type CustomElement = Transformable & {
  content?: string;
  color?: string;
};

function MyApp() {
  const [elements, setElements] = useState<CustomElement[]>([
    { 
      id: '1', 
      x: 100, 
      y: 100, 
      width: 120, 
      height: 80, 
      rotation: 0, 
      origin: [0, 0], 
      zIndex: 0,
      content: 'Element 1',
      color: '#e3f2fd'
    },
    { 
      id: '2', 
      x: 300, 
      y: 150, 
      width: 100, 
      height: 100, 
      rotation: 45, 
      origin: [0, 0], 
      zIndex: 0,
      content: 'Element 2',
      color: '#f3e5f5'
    },
  ]);

  const updateElement = (id: string, updates: Partial<CustomElement>) => {
    setElements(prev => prev.map(el => 
      el.id === id ? { ...el, ...updates } : el
    ));
  };

  return (
    <TransformableProvider
      elements={elements}
      updateElement={updateElement}
    >
      <div className="canvas" data-canvas style={{ width: '800px', height: '600px', border: '1px solid #ccc' }}>
        {elements.map(element => (
          <TransformableItem
            key={element.id}
            id={element.id}
          >
            <div style={{ 
              backgroundColor: element.color, 
              width: '100%', 
              height: '100%',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center'
            }}>
              {element.content}
            </div>
          </TransformableItem>
        ))}
      </div>
    </TransformableProvider>
  );
}

API Reference

TransformableProvider

The main context provider that manages selection state and transformation logic.

Props

  • elements: Transformable[] - Array of all transformable elements
  • updateElement: (id: string, updates: Partial<Transformable>) => void - Function to update an element
  • gridSize?: number - Grid size for snapping (in pixels), defaults to 1 (no snapping)
  • snapToGrid?: boolean - Whether to snap elements to grid, defaults to false
  • theme?: TransformableTheme - Custom theme for styling
  • showOrigin?: boolean - Whether to show transform origin indicators

TransformableItem

A single transformable element component that automatically reads all necessary data from the TransformableProvider context.

Props

  • id: string - The unique identifier of the element to render
  • children?: React.ReactNode - The content to render inside the element

Usage

// Simple text content
<TransformableItem id="element-1">
  {element.id}
</TransformableItem>

// Custom HTML content
<TransformableItem id="element-2">
  <div style={{ padding: '10px' }}>
    <h3>Custom Title</h3>
    <p>Custom description</p>
  </div>
</TransformableItem>

// Image content
<TransformableItem id="element-3">
  <img src={element.src} alt="Custom image" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
</TransformableItem>

Note: TransformableItem automatically reads the element's transformation data (position, size, rotation, etc.) from the context using the provided id. No need to pass element data as props!

Transformable Interface

interface Transformable {
  id: string;
  x: number;
  y: number;
  width: number;
  height: number;
  rotation: number;
  origin: [number, number]; // Transform origin [x, y] as percentages
  zIndex: number;
  minWidth?: number; // Minimum width constraint (allows negative values if not set)
  minHeight?: number; // Minimum height constraint (allows negative values if not set)
}

// For custom elements, extend the base interface:
type CustomElement = Transformable & {
  content?: string;
  color?: string;
  // ... your custom properties
};

Usage Examples

Custom Element Content

<TransformableItem
  element={element}
  isSelected={selectedIds.has(element.id)}
>
  <div style={{ padding: '10px' }}>
    <h3>Custom Content</h3>
    <p>This element has custom content!</p>
  </div>
</TransformableItem>

Different Element Types

{elements.map(element => (
  <TransformableItem
    key={element.id}
    element={element}
    isSelected={selectedIds.has(element.id)}
  >
    {element.type === 'image' ? (
      <img src={element.src} alt={element.alt} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
    ) : element.type === 'text' ? (
      <div style={{ padding: '10px', fontSize: element.fontSize }}>
        {element.id}
      </div>
    ) : (
      element.id
    )}
  </TransformableItem>
))}

Features in Detail

Multi-Selection

  • Single Selection: Click on an element to select it
  • Multi-Selection: Hold Shift and click to add/remove elements from selection
  • Selection Box: Drag on empty space to create a selection box (coming soon)

Transformation Handles

  • Resize Handles: White squares on corners and edges for resizing
  • Rotation Handles: Invisible handles beyond corners for rotation
  • Drag Area: Click and drag anywhere on the element to move it

Grid Snapping

  • Toggle grid snapping on/off
  • Configure grid size
  • Elements snap to grid during drag, resize, and rotate operations

Real-time Feedback

  • All transformations provide immediate visual feedback
  • Multi-drag shows all selected elements moving together
  • Smooth animations and responsive interactions

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

Publishing to npm

To publish this library to npm:

  1. Update version: Update the version in package.json
  2. Build: Run npm run build:lib to build the library
  3. Test: Run npm publish --dry-run to see what would be published
  4. Publish: Run npm publish to publish to npm

Build Process

The library is built using Vite's library mode and includes:

  • ESM and CommonJS formats
  • TypeScript declarations (.d.ts files)
  • Source maps for debugging
  • Tree-shaking support

Package Structure

react-transformable/
├── dist/
│   ├── index.esm.js      # ES Module bundle
│   ├── index.js          # CommonJS bundle
│   ├── index.d.ts        # TypeScript declarations
│   └── themes/
│       └── index.d.ts    # Theme declarations
├── src/                  # Source code
├── demo/                 # Demo application
└── README.md            # This file

License

MIT

Theming

The library supports comprehensive theming for all visual aspects of transformable items. You can customize element outlines, resize handlers, rotation handlers, and origin indicators with ease.

Styling Units

All borders and outlines use px units for consistent visual thickness. The library is designed to work well at various zoom levels while maintaining the intended visual appearance.

Basic Theme Structure

import { TransformableProvider, type TransformableTheme } from 'react-transformable';

const theme: TransformableTheme = {
  element: {
    outline: '2px solid #ff0000',
    outlineOffset: '2px',
    boxShadow: '0 0 10px rgba(255, 0, 0, 0.3)',
  },
  resizeHandlers: {
    base: {
      backgroundColor: '#ff6b6b',
      outline: '2px solid #fff',
      borderRadius: '50%',
      width: 12,
      height: 12,
    },
    // Override specific handlers if needed
    'top-left': {
      backgroundColor: '#4ecdc4', // Different color for top-left
    },
  },
  rotationHandler: {
    base: {
      backgroundColor: '#4ecdc4',
      border: '2px solid #fff',
      borderRadius: '50%',
      width: 12,
      height: 12,
    },
  },
  origin: {
    backgroundColor: '#ff6b6b',
    border: '2px solid #fff',
    width: '8px',
    height: '8px',
  },
};

Using Base Styles for Efficiency

The theme system supports base styles that apply to all handlers, making customization efficient:

const efficientTheme: TransformableTheme = {
  resizeHandlers: {
    base: {
      backgroundColor: '#ff6b6b',
      outline: '2px solid #fff',
      borderRadius: '50%',
      width: 12,
      height: 12,
    },
    // Only override what's different
    'top-left': { backgroundColor: '#4ecdc4' },
    'bottom-right': { backgroundColor: '#4ecdc4' },
  },
};

Applying Themes

<TransformableProvider
  selected={selectedIds}
  setSelectedIds={setSelectedIds}
  gridSize={gridSize}
  snapToGrid={snapToGrid}
  elements={elements}
  updateElement={updateElement}
  theme={theme} // Pass your custom theme
>
  {/* Your transformable items will use the custom theme */}
</TransformableProvider>

Default Theme

If no theme is provided, the library uses a default theme with the original styling. You can also import and extend the default theme:

import { defaultTheme, type TransformableTheme } from 'react-transformable';

const customTheme: TransformableTheme = {
  ...defaultTheme, // Start with all defaults
  element: {
    ...defaultTheme.element,
    outline: '2px solid #ff0000', // Override just the outline
  },
};

Origin Display

The library can display origin indicators for elements, which are useful for understanding the rotation and transformation center points:

<TransformableProvider
  selected={selectedIds}
  setSelectedIds={setSelectedIds}
  gridSize={gridSize}
  snapToGrid={snapToGrid}
  elements={elements}
  updateElement={updateElement}
  showOrigin={true} // Enable origin display
  theme={customTheme} // Optional: customize origin appearance
>
  {/* Your transformable items will show origin indicators */}
</TransformableProvider>

Origin Indicator Features:

  • Shows the exact center point of rotation for each element
  • Positioned based on the element's origin property
  • Rotates with the element to maintain visual alignment
  • Customizable through the theme system
  • High z-index to ensure visibility above other elements