Why Isn’t Clipboard.write() Copying My RichText/HTML?
Having issue about copying HTML to the clipboard in JavaScript using Clipboard.write()? So did I, and this write up follows how I solved the issue.

I was working on bookmarklets one afternoon and instead of using the now deprecated document.execCommand("copy")
I wanted to try the new Clipboard.write()
call being introduced into the web standards.
However, some of what I wanted to copy was formatted HTML with the code looking something like:
const richTextDiv = document.getElementById("richTextDiv");
const clipboardItem = new ClipboardItem({
"text/html": new Blob(
[richTextDiv.outerHTML],
{ type: "text/html" }
),
});
navigator.clipboard.write([clipboardItem]);
On running this code nothing ended up on the clipboard. Making sure the API did work on Chrome 98 (the latest at the time) I tested the Clipboard.writeText()
call, which worked as expected for plaintext.
An afternoon worth of reading StackOverflow, the W3 spec, release notes, and a look into clipboard-polyfill I realised that all examples (bar one Safari one) also included a fallback text/plain
version. Then digging deeper into the spec:
[...] Implementations should create alternate text/html and text/plain clipboard formats [...]
This means the following at the time of writing this:
- If the MIME Type of the text is
text/html
then also have a fallback of typetext/plain
. - That the clipboard can have multiple MIME Types of the same data 🤯
To then fix the code above, I would need to grab both the outerHTML
of the div
and the text representation - for this case it can just be innerText
:
const richTextDiv = document.getElementById("richTextDiv");
const clipboardItem = new ClipboardItem({
"text/plain": new Blob(
[richTextDiv.innerText],
{ type: "text/plain" }
),
"text/html": new Blob(
[richTextDiv.outerHTML],
{ type: "text/html" }
),
});
navigator.clipboard.write([clipboardItem]);
And this works as expected. If you want to know more, you can dig into this raw looking repo for more examples:
I hope this helped save you an afternoon of staunch debugging. If you have any feedback around this changing API and what I might be doing oddly, feel free to check my about page to see how to contact me.
Edit 1:
Looking at the Chromium Android unit tests around the clipboard, it seems to use newHtmlText()
which requires a plaintext version of the HTML being copied as well.
Edit 2:
The Chromium unit test for the async clipboard writing API also has both plain and HTML.
Edit 3:
Even by default the browser will populate at least text and HTML on the clipboard. The following was via manually copying some HTML text:

Thank you to this StackOverflow answer by Panagiotis Kanavos for helping me understand the clipboard magic for HTML.