demo · v149

Rich Text Export

A rich text editor that registers four clipboard formats — text/html, text/plain, text/csv, and application/json — all via ClipboardItem.delayed(). Only the format the paste target accepts is ever generated. No wasted serialization, no upfront cost.

Try it: Edit the table in the left panel. Click Deferred Copy then paste (Ctrl/Cmd+V) into each of the three paste targets on the right. Watch the callback log — only the format each target consumed triggers its generation callback.

Rich Table Editor

Paste Targets

HTML / Rich text
Click here, then Ctrl+V to paste rich HTML
Plain text
Click here, then Ctrl+V to paste plain text
JSON
Click here, then Ctrl+V to paste JSON

Format callback activity

Format Registered Callback fired Last paste cost
text/html
text/plain
text/csv
application/json

Callback log

Ready. Edit the table and click Deferred Copy to begin.
Copies made0
Callbacks fired0
Callbacks avoided0
Cost avoided (est.)0 ms
// Rich text editor registering 4 formats — all deferred
async function deferredCopy(editorEl) {
  const item = new ClipboardItem({
    // text/html: serialise the contenteditable innerHTML
    'text/html': ClipboardItem.delayed('text/html', async () => {
      const html = '<table>' + editorEl.innerHTML + '</table>';
      log('text/html callback fired — ' + html.length + ' chars');
      return new Blob([html], { type: 'text/html' });
    }),

    // text/plain: strip tags for plain-text paste targets
    'text/plain': ClipboardItem.delayed('text/plain', async () => {
      const plain = editorEl.innerText;
      log('text/plain callback fired — ' + plain.length + ' chars');
      return new Blob([plain], { type: 'text/plain' });
    }),

    // text/csv: walk the table cells and build CSV
    'text/csv': ClipboardItem.delayed('text/csv', async () => {
      const csv = tableToCSV(editorEl);
      log('text/csv callback fired');
      return new Blob([csv], { type: 'text/csv' });
    }),

    // application/json: structured representation
    'application/json': ClipboardItem.delayed('application/json', async () => {
      const json = JSON.stringify(tableToJSON(editorEl), null, 2);
      log('application/json callback fired');
      return new Blob([json], { type: 'application/json' });
    }),
  });

  await navigator.clipboard.write([item]);
  // None of the callbacks above have fired yet — they only run on paste.
}

// Only the format the paste target accepts will trigger its callback.
// If you copy and never paste: zero callbacks fire, zero serialization cost.

see also