Image Uploads
By default, pasted or inserted images become base64 data URLs. That’s fine for prototypes — but for real apps you almost always want a server-side upload.
The handler
<RichTextEditor
onImageUpload={async (file) => {
const form = new FormData()
form.append("image", file)
const res = await fetch("/api/upload", { method: "POST", body: form })
const { url } = await res.json()
return url
}}
/>The returned URL is inserted as <img src="…" />.
Next.js API route example
// app/api/upload/route.ts
import { NextRequest, NextResponse } from "next/server"
import { writeFile } from "node:fs/promises"
import path from "node:path"
export async function POST(req: NextRequest) {
const form = await req.formData()
const file = form.get("image") as File | null
if (!file) return NextResponse.json({ error: "no file" }, { status: 400 })
const bytes = Buffer.from(await file.arrayBuffer())
const name = `${Date.now()}-${file.name.replace(/[^a-z0-9.-]/gi, "_")}`
await writeFile(path.join(process.cwd(), "public/uploads", name), bytes)
return NextResponse.json({ url: `/uploads/${name}` })
}Validation tips
- Reject anything that isn’t
image/*server-side. - Cap file size in your handler — clients can spoof
maxLength. - Run user content through your CDN’s image optimization (resize, strip EXIF).
Error handling
onImageUpload={async (file) => {
try {
const url = await upload(file)
return url
} catch {
return "" // empty URL aborts the insertion
}
}}