Ctrl+K
v3.0.5

Advanced Usage

Deep-dive into advanced patterns, Fabric.js access, custom extensions, and performance optimization.

State Management

Imigi allows you to save and restore the complete editor state as JSON. This is useful for implementing auto-save, persisting edits across sessions, or sending the state to a server for later retrieval.

Saving State

JavaScript
const state = editor.getState();
// state is a JSON string containing all objects, filters, etc.
localStorage.setItem('editorState', state);

// or send to server
await fetch('/api/save-state', {
  method: 'POST',
  body: state,
  headers: { 'Content-Type': 'application/json' },
});

Restoring State

JavaScript
// From string
const savedState = localStorage.getItem('editorState');
if (savedState) {
  await editor.setState(savedState);
}

// From URL
await editor.setStateFromUrl('/api/get-state/123');

Custom Properties in State

By default, getState() serializes all standard Fabric.js properties. If you have added custom properties to objects, you can include them in the state by passing an array of property names.

JavaScript
// Include custom properties when saving
const state = editor.getState(['myCustomProp', 'anotherProp']);
Info

The state string can be stored in any persistence layer: localStorage, a database, or a file system. It contains everything needed to fully reconstruct the editor contents.

Accessing Fabric.js Canvas

Imigi exposes the underlying Fabric.js canvas instance through the editor.fabric property. This gives you full access to the Fabric.js API for advanced manipulation beyond what the built-in tools provide.

JavaScript
const canvas = editor.fabric;

// Get all objects
const objects = canvas.getObjects();

// Add a custom Fabric.js object
const rect = new fabric.Rect({
  left: 100,
  top: 100,
  width: 200,
  height: 150,
  fill: '#3b82f6',
  opacity: 0.8,
});
canvas.add(rect);
canvas.renderAll();

// Listen to Fabric.js events directly
canvas.on('object:modified', (e) => {
  console.log('Object modified:', e.target);
});
Warning

When adding objects directly through Fabric.js, they will not be tracked by the Imigi undo/redo history unless you manually trigger a history snapshot. Use the built-in tools API when possible for full integration with the editor's history system.

Custom Filters

You can create and register custom image filters using a 5x4 color matrix. This allows you to define unique visual effects beyond the built-in filter presets.

JavaScript
// Create a custom filter
const customFilter = {
  name: 'My Custom Filter',
  matrix: [
    0.393, 0.769, 0.189, 0, 0,
    0.349, 0.686, 0.168, 0, 0,
    0.272, 0.534, 0.131, 0, 0,
    0, 0, 0, 1, 0
  ],
};

// Register the filter
editor.tools.filter.addCustom('custom-warm', customFilter);

// Apply it
editor.tools.filter.apply('custom-warm');
Info

The matrix array is a 5x4 color transformation matrix applied via WebGL. Each row represents the output for red, green, blue, and alpha channels respectively. The fifth column is the offset value for each channel.

Custom Sticker Categories

Add your own sticker categories with custom images. You can either extend the default stickers or replace them entirely.

JavaScript
Imigi.init({
  selector: '#editor',
  tools: {
    stickers: {
      replaceDefault: false,
      items: [
        {
          name: 'Brand Assets',
          items: [
            { src: '/assets/logo.png', name: 'Company Logo' },
            { src: '/assets/badge.svg', name: 'Badge' },
            { src: '/assets/watermark.png', name: 'Watermark' },
          ],
        },
      ],
    },
  },
});

Set replaceDefault: true to completely remove the built-in stickers and only show your custom categories.

Custom Fonts

Load custom fonts (including Google Fonts) into the text tool so users can pick from your curated font list.

JavaScript
// Load Google Fonts
Imigi.init({
  selector: '#editor',
  tools: {
    text: {
      items: [
        { family: 'Inter', src: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700' },
        { family: 'Playfair Display', src: 'https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700' },
        { family: 'JetBrains Mono', src: 'https://fonts.googleapis.com/css2?family=JetBrains+Mono' },
      ],
    },
  },
});
Info

Fonts are loaded lazily when the text tool is opened. Ensure the font src URL is accessible from the user's browser. Self-hosted font files also work -- just point to your CSS file that declares the @font-face rules.

Custom Frames

Add custom decorative frames to the frame tool. Frames are PNG or SVG images that wrap around the edited image.

JavaScript
Imigi.init({
  selector: '#editor',
  tools: {
    frame: {
      replaceDefault: false,
      items: [
        {
          name: 'Polaroid',
          src: '/frames/polaroid.png',
          // Frame definition properties
        },
      ],
    },
  },
});

Like stickers, you can set replaceDefault: false to keep the built-in frames and add yours alongside them, or set it to true to replace them entirely.

Performance Optimization

Imigi uses WebGL for rendering filters and effects. On lower-end devices or with very large images, you may need to tune performance settings.

Texture Size

The textureSize option controls the maximum WebGL texture dimension. Reducing it lowers GPU memory usage at the cost of some rendering quality.

JavaScript
// Reduce for better performance on low-end devices
Imigi.init({
  selector: '#editor',
  textureSize: 2048, // Default is 4096
});

Large Images

When working with high-resolution images, keep the following tips in mind:

Memory Management

Always clean up the editor instance when you are done to release canvas memory, event listeners, and WebGL contexts.

JavaScript
// Clean up when done
editor.close();
// Set reference to null
editor = null;
Warning

Failing to call editor.close() when discarding the editor can lead to memory leaks, especially in single-page applications where the editor is mounted and unmounted repeatedly.

Security Considerations

CORS / Cross-Origin Images

When loading images from a different domain, you must enable CORS to allow the canvas to export pixel data.

JavaScript
Imigi.init({
  selector: '#editor',
  image: 'https://other-domain.com/photo.jpg',
  crossOrigin: true, // Enable CORS
});
Important

The server hosting the image must send proper CORS headers (e.g. Access-Control-Allow-Origin: *). Setting crossOrigin: true on the client side alone is not sufficient -- the server must cooperate.

Tainted Canvas

If a cross-origin image is drawn onto the canvas without CORS, the browser "taints" the canvas for security reasons. A tainted canvas cannot be exported -- calling toDataURL() or getImageData() will throw a SecurityError.

The crossOrigin option tells the browser to request the image with CORS credentials. As long as the server responds with the correct headers, the canvas remains clean and exportable.

Info

If you see a "Tainted canvases may not be exported" error in the console, it means the image server is not sending CORS headers. Either configure the server or use a proxy (see below).

Proxy for CORS

If you cannot configure CORS headers on the image server, route requests through your own backend proxy.

JavaScript
// Use a proxy to bypass CORS restrictions
const proxyUrl = '/api/proxy?url=' + encodeURIComponent(imageUrl);
Imigi.init({
  selector: '#editor',
  image: proxyUrl,
});
Warning

When using a proxy, validate and sanitize the incoming URL on the server side to prevent open-redirect or SSRF attacks. Only allow proxying to trusted image domains.

Batch Processing

You can use Imigi programmatically (with a hidden UI) to process multiple images in sequence. This is useful for applying consistent edits across a set of images.

JavaScript
async function processImages(urls) {
  const editor = await Imigi.init({
    selector: '#editor',
    ui: { visible: false },
  });

  const results = [];
  for (const url of urls) {
    await editor.resetEditor({ image: url });
    editor.tools.filter.apply('grayscale');
    const dataUrl = editor.tools.export.getDataUrl('jpeg', 0.8);
    results.push(dataUrl);
  }

  editor.close();
  return results;
}
Info

Setting ui.visible to false hides the editor interface. The canvas still renders in the background, so all tools and export methods remain functional.

Plugin Pattern

Create reusable editor presets for common use cases. For example, a profile picture editor that always starts with a square crop and limits the output size.

JavaScript
// Create reusable editor presets
function createProfileEditor(selector, options = {}) {
  return Imigi.init({
    selector,
    ui: {
      defaultTool: 'crop',
      nav: {
        replaceDefault: true,
        items: [
          { name: 'crop', action: 'crop' },
          { name: 'filter', action: 'filter' },
          { name: 'finetune', action: 'finetune' },
        ],
      },
    },
    tools: {
      crop: {
        defaultRatio: '1:1',
        presets: [{ ratio: '1:1', name: 'Square' }],
      },
      resize: {
        maxWidth: 500,
        maxHeight: 500,
      },
    },
    ...options,
  });
}

// Use it
const editor = await createProfileEditor('#editor', {
  image: 'avatar.jpg',
  onSave: (data) => uploadAvatar(data),
});

This pattern lets you encapsulate editor configurations as composable factory functions, making it easy to maintain consistent behavior across your application.

Server-Side Rendering (SSR) Considerations

Imigi requires browser APIs (window, document, canvas) that are not available in server-side environments like Node.js. When using SSR frameworks such as Next.js, Nuxt, or Remix, you must ensure Imigi only loads on the client.

Check for Window

JavaScript
if (typeof window !== 'undefined') {
  const { Imigi } = await import('imigi');
  await Imigi.init({ selector: '#editor', image: 'photo.jpg' });
}

Dynamic Imports (Next.js)

JavaScript
import dynamic from 'next/dynamic';

const EditorComponent = dynamic(
  () => import('../components/Editor'),
  { ssr: false }
);
Info

For detailed framework-specific examples, see the Integrations page which covers Next.js, Nuxt, and other SSR frameworks.

Debugging

When troubleshooting issues, you can inspect the editor's internal state and canvas objects using these helper methods.

JavaScript
// Access internal state
console.log(editor.state);

// Check dirty status
console.log('Has changes:', editor.isDirty());

// List all objects
console.log('Objects:', editor.tools.objects.getAll());

// Current zoom level
console.log('Zoom:', editor.tools.zoom.getLevel());
Info

editor.isDirty() returns true if the editor has unsaved changes since the last save or load. This is useful for prompting users before they navigate away from a page with unsaved work.

Warning

Avoid relying on internal state properties in production code. The internal structure may change between versions. Use the documented API methods instead.

On This Page
State Management Accessing Fabric.js Canvas Custom Filters Custom Sticker Categories Custom Fonts Custom Frames Performance Optimization Security Considerations Batch Processing Plugin Pattern SSR Considerations Debugging