Next.js

Editpix works with the App Router (Next 13, 14, 15, 16). The canvas engine runs in the browser, so you want ssr: false.

Install

Terminal
npm install @editpix/react

Dynamic import

Keep Editpix out of your server bundle to shave ~45KB gzipped off your initial response.

app/edit/page.tsx
"use client";

import dynamic from "next/dynamic";

const EditpixEditor = dynamic(
  () => import("@editpix/react").then((m) => m.EditpixEditor),
  { ssr: false, loading: () => <EditorSkeleton /> },
);

export default function EditPage({
  searchParams,
}: {
  searchParams: { url: string };
}) {
  return (
    <main className="h-[calc(100vh-64px)] p-6">
      <EditpixEditor
        apiKey={process.env.NEXT_PUBLIC_EDITPIX_KEY!}
        imageUrl={searchParams.url}
        onSave={async (blob) => {
          const form = new FormData();
          form.append("file", blob);
          const res = await fetch("/api/upload", { method: "POST", body: form });
          const { url } = await res.json();
          window.location.href = `/preview?url=${encodeURIComponent(url)}`;
        }}
      />
    </main>
  );
}

Route handler for uploads

app/api/upload/route.ts
import { NextResponse } from "next/server";
import { put } from "@vercel/blob";

export async function POST(req: Request) {
  const form = await req.formData();
  const file = form.get("file") as File;
  const { url } = await put(`edited/${crypto.randomUUID()}.png`, file, {
    access: "public",
  });
  return NextResponse.json({ url });
}
CSP tip. If you ship a strict Content Security Policy, add 'wasm-unsafe-eval' to script-src. Editpix uses a small WebAssembly decoder for JPEG progressive scans.