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

@siali/sorolla

v1.3.0

Published

Sorolla components library for Siali

Readme

Sorolla - React Annotation Library

Sorolla is a powerful React component library for image annotation, built with Konva.js. It provides efficient canvas-based annotation tools with support for rectangles, polygons, lines, and points, optimized for both vanilla React and Next.js projects.

Features

  • 🎯 Multiple Annotation Types: Rectangle, Polygon, Line, and Point annotations
  • 🎨 Customizable Styling: Global and per-annotation styling options
  • ⌨️ Keyboard Shortcuts: Intuitive keyboard navigation and tool switching
  • 🔄 Real-time Updates: Live annotation updates with callback support
  • 📱 Responsive Design: Adapts to container size changes
  • 🎛️ Tool Management: Built-in tools for editing, moving, and creating annotations
  • 📦 TypeScript Support: Full type safety and IntelliSense support
  • 🚀 Performance Optimized: Efficient rendering with Konva.js

Installation

Install the required peer dependencies:

npm install react react-dom konva @siali/sorolla
# or
yarn add react react-dom konva @siali/sorolla
# or
pnpm add react react-dom konva @siali/sorolla

Quick Start

Basic Usage in React (with Provider)

import React, { useState } from 'react';
import { SorollaAnnotator, Rectangle, AnnotationsProvider } from '@siali/sorolla';

const initialAnnotations: Rectangle[] = [
  {
    id: "1",
    position: { x: 100, y: 100 },
    width: 200,
    height: 150,
    type: "rectangle",
  },
];

export default function MyAnnotator() {
  const [annotations, setAnnotations] = useState<Rectangle[]>(initialAnnotations);

  return (
    <div style={{ width: '800px', height: '600px' }}>
      <AnnotationsProvider>
        <SorollaAnnotator
          image="/path/to/your/image.jpg"
          initialAnnotations={annotations}
          onUpdate={setAnnotations}
        />
      </AnnotationsProvider>
    </div>
  );
}

Next.js Usage (with Provider)

For Next.js projects, use dynamic imports to avoid SSR issues:

"use client";

import dynamic from "next/dynamic";
import { Suspense } from "react";

const SorollaAnnotator = dynamic(
  () => import("@siali/sorolla").then((mod) => mod.SorollaAnnotator),
  {
    ssr: false,
    loading: () => <div>Loading annotator...</div>,
  }
);
const AnnotationsProvider = dynamic(
  () => import("@siali/sorolla").then((mod) => mod.AnnotationsProvider),
  {
    ssr: false,
    loading: () => <div>Loading provider...</div>,
  }
);

export default function NextJSAnnotator() {
  return (
    <div style={{ width: '800px', height: '600px' }}>
      <Suspense fallback={<div>Loading...</div>}>
        <AnnotationsProvider>
          <SorollaAnnotator
            image="/path/to/your/image.jpg"
            initialAnnotations={[]}
            onUpdate={(annotations) => console.log('Updated:', annotations)}
          />
        </AnnotationsProvider>
      </Suspense>
    </div>
  );
}

API Reference

SorollaAnnotator Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | image | string | - | Required. Path or URL to the image to annotate | | initialAnnotations | Annotation[] | [] | Initial annotations to display | | onUpdate | (annotations: Annotation[]) => void | - | Callback fired when annotations change | | backgroundColor | string | - | Background color for the canvas | | fullScreen | boolean | false | Whether to use full screen mode | | isLoading | boolean | false | Show loading state | | style | CSSProperties | - | Custom styles for the container | | className | string | - | CSS class name for the container | | width | number | - | Fixed width (overrides responsive behavior) | | height | number | - | Fixed height (overrides responsive behavior) |

Annotation Types

Rectangle Annotation

interface Rectangle {
  id: string;
  type: 'rectangle';
  position: { x: number; y: number };
  width: number;
  height: number;
  color?: string;
  opacity?: number;
  strokeWidth?: number;
  visible?: boolean;
  label?: string;
  data?: any;
}

Polygon Annotation

interface Polygon {
  id: string;
  type: 'polygon';
  points: number[]; // [x1, y1, x2, y2, ...]
  color?: string;
  opacity?: number;
  strokeWidth?: number;
  visible?: boolean;
  label?: string;
  data?: any;
}

Line Annotation

interface Line {
  id: string;
  type: 'line';
  points: number[]; // [x1, y1, x2, y2, ...]
  color?: string;
  opacity?: number;
  strokeWidth?: number;
  visible?: boolean;
  label?: string;
  data?: any;
}

Point Annotation

interface Point {
  id: string;
  type: 'point';
  position: { x: number; y: number };
  color?: string;
  opacity?: number;
  strokeWidth?: number;
  visible?: boolean;
  label?: string;
  data?: any;
}

Advanced Usage

Using Context Hooks

The library provides several context hooks for advanced control:

import { useCanvas, useAnnotations } from '@siali/sorolla';

function AnnotationControls() {
  const { 
    annotations, 
    setAnnotations, 
    setAnnotationAttributes, 
    annotationAttributes,
    tool,
    setTool,
    zoomIn,
    zoomOut,
    undo,
    redo
  } = useCanvas();
  const { imageDimensions } = useAnnotations();

  const handleUpdateStyle = () => {
    setAnnotationAttributes({
      color: '#ff0000',
      opacity: 0.5,
      strokeWidth: 3,
      label: 'updated',
      visible: true,
    });
  };

  const handleDeleteAll = () => {
    setAnnotations([]);
  };

  return (
    <div className="controls">
      <button onClick={() => setTool('bbox')}>Rectangle Tool</button>
      <button onClick={() => setTool('polygon')}>Polygon Tool</button>
      <button onClick={() => setTool('line')}>Line Tool</button>
      <button onClick={() => setTool('point')}>Point Tool</button>
      <button onClick={() => setTool('move')}>Move Tool</button>
      
      <button onClick={zoomIn}>Zoom In</button>
      <button onClick={zoomOut}>Zoom Out</button>
      
      <button onClick={undo}>Undo</button>
      <button onClick={redo}>Redo</button>
      
      <button onClick={handleUpdateStyle}>Update Style</button>
      <button onClick={handleDeleteAll}>Delete All</button>
      
      <p>Current tool: {tool}</p>
      <p>Annotations count: {annotations.length}</p>
      <p>Real image size: {imageDimensions.realDimensions.width} x {imageDimensions.realDimensions.height}</p>
    </div>
  );
}

Custom Annotation Attributes

Set global annotation attributes that apply to all new annotations:

const { setAnnotationAttributes } = useCanvas();

// Update global attributes
setAnnotationAttributes({
  color: '#00ff00',
  opacity: 0.7,
  strokeWidth: 2,
  label: 'custom-label',
  visible: true,
});

Annotation Data Handling

The onUpdate callback receives the complete annotations array whenever changes occur:

function handleAnnotationUpdate(annotations: Annotation[]) {
  // Process annotations
  console.log('Total annotations:', annotations.length);
  
  // Filter by type
  const rectangles = annotations.filter(ann => ann.type === 'rectangle');
  const polygons = annotations.filter(ann => ann.type === 'polygon');
  
  // Save to backend
  saveAnnotations(annotations);
  
  // Update local state
  setLocalAnnotations(annotations);
}

<SorollaAnnotator
  image="/image.jpg"
  initialAnnotations={annotations}
  onUpdate={handleAnnotationUpdate}
/>

Working with Different Annotation Types

import { Rectangle, Polygon, Line, Point, Annotation } from '@siali/sorolla';

// Type-safe annotation handling
function processAnnotations(annotations: Annotation[]) {
  annotations.forEach(annotation => {
    switch (annotation.type) {
      case 'rectangle':
        const rect = annotation as Rectangle;
        console.log(`Rectangle: ${rect.width}x${rect.height} at (${rect.position.x}, ${rect.position.y})`);
        break;
        
      case 'polygon':
        const poly = annotation as Polygon;
        console.log(`Polygon with ${poly.points.length / 2} points`);
        break;
        
      case 'line':
        const line = annotation as Line;
        console.log(`Line with ${line.points.length / 2} points`);
        break;
        
      case 'point':
        const point = annotation as Point;
        console.log(`Point at (${point.position.x}, ${point.position.y})`);
        break;
    }
  });
}

Keyboard Shortcuts

| Key | Action | |-----|--------| | V | Switch to Edit tool | | B | Switch to Rectangle (Bbox) tool | | P | Switch to Polygon tool | | M | Switch to Move tool | | O | Switch to Point tool | | L | Switch to Line tool | | Space | Temporarily switch to Move tool (hold) | | Escape | Deselect current annotation | | I or + | Zoom in | | O or - | Zoom out | | Ctrl + Mouse Wheel | Zoom | | Mouse Wheel | Pan (when in move mode) |

Styling and Customization

Default Annotation Attributes

const defaultAttributes = {
  color: '#D3D3D3',
  opacity: 0.3,
  strokeWidth: 1,
  visible: true,
  label: "new",
};

Custom Styling

<SorollaAnnotator
  image="/image.jpg"
  style={{
    border: '2px solid #ccc',
    borderRadius: '8px',
    boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
  }}
  className="my-annotator"
/>

Error Handling

function SafeAnnotator() {
  const [error, setError] = useState<string | null>(null);
  
  const handleError = (error: Error) => {
    console.error('Annotation error:', error);
    setError(error.message);
  };
  
  if (error) {
    return (
      <div className="error">
        <p>Error: {error}</p>
        <button onClick={() => setError(null)}>Retry</button>
      </div>
    );
  }
  
  return (
    <SorollaAnnotator
      image="/image.jpg"
      initialAnnotations={[]}
      onUpdate={(annotations) => {
        try {
          // Process annotations safely
          processAnnotations(annotations);
        } catch (err) {
          handleError(err as Error);
        }
      }}
    />
  );
}

Performance Tips

  1. Optimize Images: Use WebP format and appropriate sizes
  2. Limit Annotations: For large datasets, consider virtualization
  3. Debounce Updates: Debounce the onUpdate callback for frequent changes
  4. Memoize Components: Use React.memo for wrapper components
const DebouncedAnnotator = React.memo(({ onUpdate, ...props }) => {
  const debouncedUpdate = useMemo(
    () => debounce(onUpdate, 300),
    [onUpdate]
  );
  
  return (
    <SorollaAnnotator
      {...props}
      onUpdate={debouncedUpdate}
    />
  );
});

Troubleshooting

Common Issues

  1. SSR Errors in Next.js: Use dynamic imports with ssr: false
  2. Canvas Not Rendering: Ensure container has explicit dimensions
  3. Annotations Not Updating: Check that onUpdate callback is provided
  4. Performance Issues: Reduce annotation count or optimize image size

Debug Mode

Enable debug logging:

// Add to your component
useEffect(() => {
  console.log('Current annotations:', annotations);
}, [annotations]);

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

License

MIT License - see LICENSE file for details.

Support

For issues and questions:


Sorolla - Making image annotation simple and powerful in React applications.