Overview
The ImageEditor component provides a powerful image editing interface with support for filters, effects, text, shapes, and more.
Import
'use client' ;
import dynamic from 'next/dynamic' ;
const ImageEditor = dynamic (
() => import ( '@distralabs/media-editor' ). then ( mod => ({ default: mod . ImageEditor })),
{ ssr: false }
);
Always use dynamic import with ssr: false to avoid server-side rendering issues.
Props
Required Props
Your SDK license key (JWT format). Contact your account manager to obtain a license key. licenseKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Callback function called when the user closes the editor. onClose = {() => setShowEditor ( false )}
Optional Props
Override the default license validation API endpoint. Default: https://api.kloudleads.com/license/validateapiUrl = "https://localhost:3030/social"
The SDK appends /license/validate to this URL. If your endpoint is https://localhost:3030/social/license/validate, use apiUrl="https://localhost:3030/social".
Initial image file to load into the editor. const [ file , setFile ] = useState < File | null >( null );
< ImageEditor
files = { file }
// ...
/>
callback
(result: CallbackProps, extras?: EditorExtras) => void
Callback function called when the user exports the edited image. CallbackProps: interface CallbackProps {
base64 : string ; // Data URL of exported image
width : number ; // Canvas width
height : number ; // Canvas height
template ?: any ; // Scene data if template used
}
EditorExtras: interface EditorExtras {
thumbnail ?: string ; // Thumbnail preview
format ?: string ; // Image format (png, jpg, etc)
}
Example: const handleExport = ( result : CallbackProps , extras ?: EditorExtras ) => {
// Download the image
const link = document . createElement ( 'a' );
link . href = result . base64 ;
link . download = 'edited-image.png' ;
link . click ();
// Or upload to server
fetch ( '/api/upload' , {
method: 'POST' ,
body: JSON . stringify ({ image: result . base64 }),
});
};
Custom theme object to override default colors and styling. const customTheme = {
'background.primary' : '#0f172a' ,
'background.secondary' : '#1e293b' ,
'text.primary' : '#ffffff' ,
'accent.primary' : '#3b82f6' ,
};
< ImageEditor theme = { customTheme } />
See Theme Customization for all available keys.
Show theme customization UI to users. Set to false for production. showThemeCreator = { false } // Hide theme UI
Enable headless (programmatic) mode. The editor will execute predefined functions without showing the UI. < ImageEditor
headless = { true }
calls = { [
() => handleAddText ([ 'Headline' ]),
() => handleAddShape ( 'rectangle' ),
]}
/>
Array of functions to execute in headless mode. calls = { [
() => handleAddText ([ 'Text 1' , 'Text 2' ]),
() => handleAddShape ( 'rectangle' ),
() => applyFilter ( 'grayscale' ),
]}
Array of brand presets for consistent styling. interface BrandDetails {
id : string ;
name : string ;
primaryColor ?: string ;
secondaryColor ?: string ;
fontFamily ?: string ;
logo ?: string ;
}
const brands = [
{
id: 'brand-1' ,
name: 'My Brand' ,
primaryColor: '#3b82f6' ,
fontFamily: 'Inter' ,
},
];
< ImageEditor brands = { brands } />
Load a template on editor startup. interface Template {
id ?: string ;
name ?: string ;
sceneData : EditorItem [];
width : number ;
height : number ;
thumbnail ?: string ;
brandId ?: string ;
}
< ImageEditor defaultTemplate = { myTemplate } />
onExport
(templateId: string, brandResults: any, updatedImage: string, thumbUri: string) => void
Advanced export callback with additional metadata. const handleExport = ( templateId , brandResults , updatedImage , thumbUri ) => {
console . log ( 'Template ID:' , templateId );
console . log ( 'Brand results:' , brandResults );
console . log ( 'Image:' , updatedImage );
console . log ( 'Thumbnail:' , thumbUri );
};
onSaveTemplate
(props: { brandId: string; template: Template }) => Promise<void>
Callback when user saves a template. const handleSaveTemplate = async ({ brandId , template }) => {
await fetch ( '/api/templates' , {
method: 'POST' ,
body: JSON . stringify ({ brandId , template }),
});
};
onGetTemplates
(brandIdList?: string[]) => Promise<{ success: { data: Template[] } }>
Callback to fetch templates for display. const handleGetTemplates = async ( brandIds ) => {
const response = await fetch ( '/api/templates?' + new URLSearchParams ({
brandIds: brandIds ?. join ( ',' ) || ''
}));
return response . json ();
};
AI-powered content creation function (advanced feature).
Complete Example
'use client' ;
import { useState , useCallback , useRef } from 'react' ;
import dynamic from 'next/dynamic' ;
const ImageEditor = dynamic (
() => import ( '@distralabs/media-editor' ). then ( mod => ({ default: mod . ImageEditor })),
{ ssr: false }
);
const customTheme = {
'background.primary' : '#0f172a' ,
'background.secondary' : '#1e293b' ,
'text.primary' : '#ffffff' ,
'accent.primary' : '#3b82f6' ,
};
export default function ImageStudioPage () {
const fileInputRef = useRef < HTMLInputElement >( null );
const [ selectedFile , setSelectedFile ] = useState < File | null >( null );
const [ showEditor , setShowEditor ] = useState ( false );
const [ exportedImage , setExportedImage ] = useState < string | null >( null );
const handleFileSelect = ( e : React . ChangeEvent < HTMLInputElement >) => {
const file = e . target . files ?.[ 0 ];
if ( file && file . type . startsWith ( 'image/' )) {
setSelectedFile ( file );
setShowEditor ( true );
}
};
const handleExport = useCallback (( result : any ) => {
if ( result . base64 ) {
setExportedImage ( result . base64 );
setShowEditor ( false );
// Download
const link = document . createElement ( 'a' );
link . href = result . base64 ;
link . download = `edited- ${ Date . now () } .png` ;
link . click ();
}
}, []);
const handleClose = () => {
setShowEditor ( false );
setSelectedFile ( null );
};
return (
< div className = "min-h-screen p-6" >
{! showEditor && (
< div >
< h1 className = "text-3xl font-bold mb-4" > Image Studio </ h1 >
< input
ref = { fileInputRef }
type = "file"
accept = "image/*"
onChange = { handleFileSelect }
className = "hidden"
/>
< button
onClick = {() => fileInputRef.current?.click()}
className = "px-6 py-3 bg-blue-500 text-white rounded-lg"
>
Choose Image
</ button >
</ div >
)}
{ showEditor && selectedFile && (
< div className = "fixed inset-0 z-50" >
< ImageEditor
licenseKey = {process.env.NEXT_PUBLIC_LICENSE_KEY || '' }
apiUrl = "https://localhost:3030/social"
files = { selectedFile }
onClose = { handleClose }
callback = { handleExport }
theme = { customTheme }
showThemeCreator = { false }
/>
</ div >
)}
</ div >
);
}
Features
Image Editing
Upload & Load: Support for JPG, PNG, GIF, WebP
Resize: Custom dimensions or preset aspect ratios
Crop: Freeform or ratio-locked cropping
Rotate: 90° increments or custom angles
Flip: Horizontal and vertical flipping
Effects & Filters
Color Filters: Grayscale, Sepia, Invert, Colorize
Adjustments: Brightness, Contrast, Saturation, Hue
Blur: Gaussian blur with adjustable radius
Artistic: Pixelate, Noise, Emboss, Posterize
Shadows: Drop shadows with offset, blur, color
Text
Rich Text: Multiple fonts, sizes, colors, weights
Google Fonts: Dynamic font loading
Formatting: Bold, italic, underline, alignment
Effects: Shadows, outlines, backgrounds
Shapes
Basic Shapes: Rectangle, Circle, Triangle, Star, Arrow
Customization: Fill color, stroke, stroke width
Advanced: Custom paths and SVG shapes
Layers
Multi-layer Support: Unlimited layers
Z-Index Control: Bring forward, send backward
Layer Management: Show/hide, lock, duplicate, delete
Background
Solid Colors: Any color picker
Gradients: Linear and radial gradients
Images: Background image with fit modes
Export
Formats: PNG, JPG, WebP
Quality: Adjustable compression
Dimensions: Original or custom size
Browser Support
Chrome 90+
Firefox 88+
Safari 14+
Edge 90+
Mobile browsers have limited support. Desktop browsers recommended for best experience.
Next Steps
Integration Guide Step-by-step Next.js integration
VideoEditor API VideoEditor component reference
Troubleshooting Common issues and solutions
Quickstart Build your first editor