Zero dependencies · TypeScript · ~0.8kB gzip

Normalize every
paste event

Images, files, rich HTML, plain text — every clipboard paste resolved into one predictable shape: { type, data, files }. No guesswork.

✦ Try the live demo
Images & screenshots File pastes Rich HTML Plain text ~0.8kB gzip
Why paste-rich

Everything you need, nothing you don't

🎯

One predictable shape

Every paste becomes { type, data, files }. No more guessing which clipboardData API to call.

🖼️

Images first-class

Screenshots, pasted PNGs, copied images from the web — all detected as type: 'image' with a File you can preview or upload.

📁

Multi-file support

Paste from a file explorer and get all files via result.files. Image or document — classified automatically.

🔀

Smart priority

Image > File > HTML > Text. When Word pastes include both HTML and text, you get the richest format by default.

🎛️

Type filtering

Only care about images? Pass types: ['image']. Unmatched pastes fall through untouched.

🚫

Zero dependencies

Pure TypeScript class you drop into any project. No framework coupling, no bloat. ~0.8kB gzipped.

Interactive Demo

Paste anything right here

Click the zone below, then paste — see the normalized result

Try pasting a screenshot, a file, rich text from a document, or plain text.

📋
Click here, then press Ctrl+V or +V
Get Started

Install in 30 seconds

npm install paste-rich
yarn add paste-rich
pnpm add paste-rich
<script type="module">
  import PasteRich from 'https://esm.sh/paste-rich';
</script>
import PasteRich from 'paste-rich';

const pr = new PasteRich({
  target: '#editor',
  onPaste: (result) => {
    console.log(result.type);  // 'image' | 'file' | 'html' | 'text'
    console.log(result.data);  // File or string
    console.log(result.files); // File[]
  },
});
import PasteRich from 'paste-rich';

const pr = new PasteRich({
  target: '#drop-zone',
  types: ['image'],
  onPaste: ({ data }) => {
    const url = URL.createObjectURL(data);
    document.getElementById('preview').src = url;
  },
});
import { useEffect, useRef } from 'react';
import PasteRich from 'paste-rich';

function usePasteRich(ref, onPaste) {
  const pr = useRef(null);

  useEffect(() => {
    if (!ref.current) return;
    pr.current = new PasteRich({
      target: ref.current,
      onPaste,
    });
    return () => pr.current?.destroy();
  }, [ref, onPaste]);
}
import PasteRich from 'paste-rich';

new PasteRich({
  types: ['image'],
  onPaste: async ({ data }) => {
    const form = new FormData();
    form.append('image', data);
    await fetch('/api/upload', {
      method: 'POST',
      body: form,
    });
  },
});
API Reference

Constructor options

Option Type Default Description
target HTMLElement | string document Element or CSS selector to listen for paste events on.
onPaste (result: PasteResult) => void Required. Called with the normalized paste result.
types PasteType[] all Filter to specific types. Unmatched pastes are ignored.
preventDefault boolean true Whether to prevent the default paste behavior.

PasteResult & methods

Member Type Description
result.type 'image' | 'file' | 'html' | 'text' Detected paste type (highest priority match).
result.data string | File Primary data. File for image/file, string for html/text.
result.files File[] All files from the paste. Empty for text/html-only.
.destroy() void Remove the paste listener and clean up.
[Symbol.dispose]() void Alias for destroy(). Enables using syntax.