Creating a Next.js Image Editor with glfx.js
1 April 202519 min read
Modern web applications often require real-time image processing capabilities. With Next.js for performance and glfx.js for GPU-accelerated image effects, we can create a fast and responsive image editor with various filters and transformations.
In this guide, we’ll cover:
- Setting up a Next.js project
- Integrating glfx.js for real-time image effects
- Implementing file uploads
- Adding filters and transformations
- Optimizing performance
Let’s get started!
Step 1: Set Up a Next.js Project
First, create a new Next.js project:
Install glfx.js for image processing:
Initialize ShadCN UI
To set up ShadCN UI for a modern interface, install it with:
Then, install the necessary components:
npx shadcn-ui@latest add button select slider card label tabs
Step 2: Add glfx Hook and Types
Create lib/use-glfx.ts
to handle loading glfx.js
dynamically:
Create types/glfx.d.ts
for type safety:
Create html2canvas.d.ts
to work with the html2canvas
library:
Step 3: Create the Image Editor Component
Now, create a component to handle image uploads and apply filters.
Create a new file: components/image-editor.tsx
and add the following code:
Key Features
- Uses Next.js and ShadCN UI for UI components (Select, Slider, Card, etc.).
- Loads glfx.js dynamically using the useGlfx hook.
- Applies real-time WebGL filters like brightness, contrast, sepia, vignette, etc.
- Allows users to adjust filter parameters via sliders.
- Supports saving edited images as PNGs using html2canvas.
Breakdown of Important Parts
1. Filter Configuration
The filterConfigs
object defines available filters and their parameters (range, default values).
2. State Management
selectedFilter
stores the active filter.filterParams
holds parameter values for the selected filter.texture
andcanvas
handle WebGL rendering.originalImage
stores the uploaded image.
3. Image Initialization (useEffect
)
When an image is uploaded, it:
- Loads into an
<img>
element. - Creates a WebGL canvas using
window.fx.canvas()
. - Converts the image into a
texture
. - Scales the image to fit the editor.
- Draws the texture to the canvas.
4. Applying Filters (useEffect
)
- When the user selects a filter or adjusts parameters,
canvas.draw(texture)
applies the filter dynamically. - Example:
canvas.brightnessContrast(amount, 0);
adjusts brightness.
5. Save Feature
Uses html2canvas
to take a screenshot of the WebGL editor and save it as an image.
Step 4: Implement a Fallback Editor
Not all browsers support WebGL. If glfx.js fails to load, we can provide a fallback editor using the standard Canvas API.
Create components/fallback-editor.tsx and add:
FallbackEditor
is a client-side React component that takes an imageUrl prop and lets users apply various filter effects to that image. It uses the native Canvas API rather than a specialized image editing library.
Technical Details
- Uses React's
useRef
to access the Canvas DOM element useEffect
hooks handle image loading and filter reapplication when parameters change- TypeScript is being used (notice the
HTMLCanvasElement
andHTMLImageElement
type annotations) - The component is marked with
'use client'
directive, indicating it's meant to run on the client side in Next.js
This is a relatively simple implementation of an image editor that provides basic functionality without requiring external image processing libraries.
Step 5: Add a Script Loader
Create an components/script-loader.tsx
file and add:
Your Next.js image editor depends on external JavaScript libraries like:
glfx.js
(for WebGL image effects)html2canvas
(for converting elements into images)
However, these libraries:
✅ Are not built into Next.js and must be loaded dynamically.
✅ Use browser-specific features (like WebGL and the DOM), which won’t work on the server.
✅ May fail to load in certain environments, requiring a fallback solution.
How ScriptLoader Solves These Problems
1. Prevents Crashes in Non-WebGL Browsers
- If WebGL is unsupported,
useGlfx()
setsglfxStatus
to"error"
. - The fallback UI automatically replaces the WebGL editor with the standard canvas-based editor.
2. Dynamically Loads html2canvas When Needed
- Instead of bundling html2canvas with your app, it is loaded only when required.
- This reduces initial page load time and improves performance.
3. Ensures Scripts Are Fully Loaded Before Rendering
- Prevents
"html2canvas is not defined"
errors by waiting until the script is ready. - Shows a loading message instead of a broken UI while scripts are loading.
What Happens Without ScriptLoader
?
❌ The app might crash in browsers without WebGL.
❌ html2canvas could be undefined if used before loading.
❌ Users would see blank screens instead of a graceful fallback.
Step 6: Add a Debug Canvas
For debugging purposes, add a DebugCanvas component to visualize image loading and rendering issues.
Create components/debug-canvas.tsx
and add:
This DebugCanvas component allows you to display an image on a canvas and provides the option to save the canvas as an image file. Here’s a breakdown of how it works:
1. Component Setup:
Props
: The component accepts animageUrl
prop, which is the URL of the image that you want to load and display on the canvas.Ref
: It uses theuseRef
hook to create a reference (canvasRef
) to the canvas element, which allows access to the canvas DOM node for drawing.
2. Effect Hook:
The useEffect
hook is used to load and display the image on the canvas when the imageUrl changes.
3. Image Save Functionality:
- The
handleSaveImage
function allows you to save the content of the canvas as a PNG image. - It creates a temporary
<a>
link with adownload
attribute and sets thehref
to the data URL of the canvas (which is a base64-encoded PNG of the canvas content). - The link is programmatically clicked, triggering the download, and then the link is removed from the document.
This component is useful for debugging image loading and manipulation in the browser. It helps you see the exact content of an image on the canvas and provides a way to save that content.
Step 7: Adding Layout to the Article:
Below is a refined breakdown of your code and how it fits into your article. This layout acts as a global structure around your app, so every page or component rendered as children
will inherit the layout style:
Script Loading:
The Script
component is used to include the glfx.js
library, which is critical for GPU-accelerated image editing. The strategy="afterInteractive"
ensures the script loads only after the page becomes interactive, which improves performance.
This RootLayout
would be used as the wrapper for your image editor app, ensuring consistent layout and styling across all pages in your app. The content of your article (or any other page) would be rendered as the children
prop, inheriting the layout's properties.
Step 8: Add the Editor to the Page
Now, modify page.tsx
to include the image editor. To integrate everything into page.tsx
and ensure the image editor works correctly with dynamic components like the ImageEditor
, FallbackEditor
, and ScriptLoader
, here’s a step-by-step explanation of the code:
Key Features:
1. File Selection:
The handleFileChange
function manages image selection. Once a file is chosen, it creates an object URL and stores it in imageUrl
. The URL is revoked after the component unmounts or the image changes, ensuring that no unused URLs are lingering in memory.
2. Dynamic Imports:
-
The
ImageEditor
andScriptLoader
components are dynamically imported withssr: false
, ensuring they are only loaded on the client-side. This is critical for components that depend on browser-specific APIs (like WebGL). -
If
ImageEditor
is not loaded yet, theScriptLoader
will show a fallback editor (in this case,FallbackEditor
).
3. Tabs:
- The app allows switching between two image editors—
Standard Editor
andWebGL Editor
—via tabs. This is implemented with theTabs
component, where each tab displays a different editor. - If WebGL isn't supported or fails to load, the fallback editor will be displayed.
4. Image Upload:
If no image is selected (!imageUrl
), a file input is shown that allows users to upload an image.
How It All Connects:
1. Page Flow:
- The page initially asks the user to upload an image.
- Once the image is uploaded, the user can toggle between two types of editors (standard and WebGL) using tabs.
- The
ScriptLoader
dynamically loads the WebGL editor, which is only available after the script (glfx.js) is loaded. - If WebGL fails, a fallback editor is shown.
2. User Experience:
The UI ensures a smooth experience where the user can easily upload, edit, and switch between different editors.
This setup efficiently manages dynamic loading, browser-specific behavior, and image manipulation in a React app built with Next.js.
Conclusion
The Image Editor with glfx.js provides a powerful, browser-based solution for image manipulation, combining the simplicity of standard image editing with the advanced capabilities of WebGL. By leveraging the power of glfx.js
for GPU-accelerated filters and effects, users can create stunning edits in real-time. With a smooth and user-friendly interface built using Next.js and TailwindCSS, this application ensures a seamless experience for both novice and advanced users.
Whether you're working on basic image enhancements or experimenting with creative WebGL-powered filters, this editor offers flexibility and performance. Additionally, the fallback editor ensures compatibility across a wide range of devices, ensuring your image editing experience remains robust and reliable.
Try it out today, and explore the endless possibilities for image manipulation directly in your browser!