UNPKG

@fe-daily/libimagequant-wasm

Version:

WASM bindings for libimagequant image quantization library

270 lines (210 loc) 7.26 kB
# Next.js Integration Guide This guide explains how to integrate `@fe-daily/libimagequant-wasm` with Next.js applications. ## Problem Next.js uses static analysis and bundling that doesn't work well with dynamic WASM imports using `import.meta.url`. You may encounter errors like: ``` Error: Cannot find module '/_next/static/media/libimagequant_wasm.a36650a2.js' ``` ## Solutions ### Option 1: Pre-load WASM Module (Recommended) Import the WASM module directly and pass it to the constructor: ```typescript import LibImageQuant from '@fe-daily/libimagequant-wasm'; import wasmModule from '@fe-daily/libimagequant-wasm/wasm/libimagequant_wasm.js'; // Create quantizer with pre-loaded WASM module const quantizer = new LibImageQuant({ wasmModule }); // Use normally const result = await quantizer.quantizeImageData(imageData, { maxColors: 64, speed: 3 }); ``` ### Option 2: Use in Web Worker Create a Web Worker to isolate the WASM loading: ```typescript // worker.ts import LibImageQuant from '@fe-daily/libimagequant-wasm'; let quantizer: LibImageQuant; self.onmessage = async function(e) { const { type, data } = e.data; try { if (type === 'init') { quantizer = new LibImageQuant(); self.postMessage({ type: 'ready' }); return; } if (type === 'quantize') { const result = await quantizer.quantizeImageData(data.imageData, data.options); self.postMessage({ type: 'result', result }); } } catch (error) { self.postMessage({ type: 'error', error: error instanceof Error ? error.message : String(error) }); } }; // main component import { useCallback, useEffect, useRef } from 'react'; export function useImageQuantizer() { const workerRef = useRef<Worker>(); useEffect(() => { workerRef.current = new Worker(new URL('./worker.ts', import.meta.url)); return () => workerRef.current?.terminate(); }, []); const quantize = useCallback((imageData: ImageData, options: any) => { return new Promise((resolve, reject) => { if (!workerRef.current) { reject(new Error('Worker not initialized')); return; } const handleMessage = (e: MessageEvent) => { const { type, result, error } = e.data; if (type === 'result') { resolve(result); } else if (type === 'error') { reject(new Error(error)); } workerRef.current?.removeEventListener('message', handleMessage); }; workerRef.current.addEventListener('message', handleMessage); workerRef.current.postMessage({ type: 'quantize', data: { imageData, options } }); }); }, []); return { quantize }; } ``` ### Option 3: Dynamic Import with Error Handling Use dynamic imports with proper error handling: ```typescript import { useCallback, useState } from 'react'; export function useImageQuantizer() { const [quantizer, setQuantizer] = useState<any>(null); const [loading, setLoading] = useState(false); const initialize = useCallback(async () => { if (quantizer) return quantizer; setLoading(true); try { const { default: LibImageQuant } = await import('@fe-daily/libimagequant-wasm'); const wasmModule = await import('@fe-daily/libimagequant-wasm/wasm/libimagequant_wasm.js'); const instance = new LibImageQuant({ wasmModule }); setQuantizer(instance); return instance; } catch (error) { console.error('Failed to initialize quantizer:', error); throw error; } finally { setLoading(false); } }, [quantizer]); const quantize = useCallback(async (imageData: ImageData, options: any) => { const instance = await initialize(); return instance.quantizeImageData(imageData, options); }, [initialize]); return { quantize, loading }; } ``` ## Next.js Configuration You may need to configure Next.js to handle WASM files properly: ```javascript // next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { webpack: (config, { isServer }) => { // Handle WASM files config.experiments = { ...config.experiments, asyncWebAssembly: true, }; // Handle .wasm files config.module.rules.push({ test: /\.wasm$/, type: 'webassembly/async', }); // Fallback for Node.js modules in client-side if (!isServer) { config.resolve.fallback = { ...config.resolve.fallback, fs: false, path: false, crypto: false, }; } return config; }, }; module.exports = nextConfig; ``` ## TypeScript Configuration Update your `tsconfig.json` to handle WASM imports: ```json { "compilerOptions": { "allowSyntheticDefaultImports": true, "esModuleInterop": true, "moduleResolution": "bundler" } } ``` ## Example Component ```typescript 'use client'; import { useCallback, useState } from 'react'; import LibImageQuant from '@fe-daily/libimagequant-wasm'; import wasmModule from '@fe-daily/libimagequant-wasm/wasm/libimagequant_wasm.js'; export default function ImageQuantizer() { const [result, setResult] = useState<any>(null); const [loading, setLoading] = useState(false); const handleFileUpload = useCallback(async (event: React.ChangeEvent<HTMLInputElement>) => { const file = event.target.files?.[0]; if (!file) return; setLoading(true); try { // Create canvas to get ImageData const img = new Image(); img.onload = async () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d')!; canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // Quantize with pre-loaded WASM module const quantizer = new LibImageQuant({ wasmModule }); const quantResult = await quantizer.quantizeImageData(imageData, { maxColors: 32, speed: 3 }); setResult(quantResult); }; img.src = URL.createObjectURL(file); } catch (error) { console.error('Quantization failed:', error); } finally { setLoading(false); } }, []); return ( <div> <input type="file" accept="image/*" onChange={handleFileUpload} /> {loading && <p>Processing...</p>} {result && ( <div> <p>Quantized to {result.paletteLength} colors</p> <p>Quality: {Math.round(result.quality * 100)}%</p> </div> )} </div> ); } ``` ## Troubleshooting ### Common Issues 1. **Module not found errors**: Ensure you're importing the WASM module correctly 2. **Build failures**: Check your Next.js configuration supports WASM 3. **Runtime errors**: Make sure you're running in a browser environment (use 'use client' directive) ### Tips - Always use the pre-loaded WASM module approach for the most reliable Next.js integration - Test your integration in both development and production builds - Consider using Web Workers for heavy processing to avoid blocking the UI - Make sure WASM files are properly served with correct MIME types in production