rich-text@tolipovjs

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
  }
}}