DocsGuidesHandling Image Uploads

Handling Image Uploads

Use remote URLs for production. Avoid persisting Base64 image payloads in your document JSON.

Why default Base64 flow is risky

  • Large payloads and slow save requests.
  • Higher database cost and poor caching behavior.
  • Harder image lifecycle management.

Upload then insert URL

import type { Editor } from '@tiptap/core'
import type { EditorProps } from '@tiptap/pm/view'
import { HybricEditor } from 'hybricmark'
 
async function uploadToCloudinary(file: File): Promise<string> {
  const form = new FormData()
  form.append('file', file)
  form.append('upload_preset', '<your-preset>')
 
  const res = await fetch('https://api.cloudinary.com/v1_1/<cloud-name>/image/upload', {
    method: 'POST',
    body: form,
  })
  if (!res.ok) throw new Error('Image upload failed')
 
  const data = (await res.json()) as { secure_url: string }
  return data.secure_url
}
 
const isImageFile = (file: File) => file.type.startsWith('image/')
 
async function uploadAndInsertImage(editor: Editor, file: File) {
  const url = await uploadToCloudinary(file)
  editor.chain().focus().setImage({ src: url }).run()
}
 
const editorProps: EditorProps = {
  handlePaste(view, event) {
    const files = Array.from(event.clipboardData?.files ?? []).filter(isImageFile)
    if (!files.length) return false
    event.preventDefault()
    const editor = (view as unknown as { editor?: Editor }).editor
    if (!editor) return true
    void Promise.all(files.map((file) => uploadAndInsertImage(editor, file)))
    return true
  },
  handleDrop(view, event) {
    const files = Array.from(event.dataTransfer?.files ?? []).filter(isImageFile)
    if (!files.length) return false
    event.preventDefault()
    const editor = (view as unknown as { editor?: Editor }).editor
    if (!editor) return true
    void Promise.all(files.map((file) => uploadAndInsertImage(editor, file)))
    return true
  },
}
 
export default function EditorWithUpload() {
  return <HybricEditor content="# Image Demo" editorProps={editorProps} />
}

UX tips

  • Show `Uploading...` placeholder near cursor while request is pending.
  • Show actionable error UI, not only console logs.
  • Apply server-side file validation (type/size/security scan).