December 22, 2021
While most browsers support navigator.clipboard.writeText
property
which is used to write a string to clipboard,
it gets little tricky when you want
to copy an image to clipboard.
There's no common way for copying
a blob image to clipboard for all browsers.
While working on an app that generates image from a tweet and lets you save it, I wanted to add ability to let users copy the image directly to clipboard. The implementation seemed simple until I started working on it. The problem is that browsers have different specs for writing a blob to clipboard.
In this article, we'll learn to write a function that generates image from an element and saves it in clipboard. Also since browsers handle things differently, we'll see how to make copying an image to clipboard work on Safari and Chrome(and its variants).
Below is a function that utilises domtoimage
library
to return an image blob from the element screenshotRef.current
.
We can use yarn add dom-to-image
to install the package.
Instead of screenshotRef.current
used in the function,
we can pass the id
or className
of the element we want to generate the image from.
More about how domtoimage works
can be learned here.
const snapshotCreator = () => {
return new Promise((resolve, reject) => {
try {
const scale = window.devicePixelRatio;
const element = screenshotRef.current; // You can use element's ID or Class here
domtoimage
.toBlob(element, {
height: element.offsetHeight * scale,
width: element.offsetWidth * scale,
style: {
transform: "scale(" + scale + ")",
transformOrigin: "top left",
width: element.offsetWidth + "px",
height: element.offsetHeight + "px",
},
})
.then((blob) => {
resolve(blob);
});
} catch (e) {
reject(e);
}
});
};
First, we'll need a check to see if the browser is Safari. You can use below check to detect Safari.
const isSafari = /^((?!chrome|android).)*safari/i.test(
navigator?.userAgent
);
Now that we have the check, we can use the below function to copy the image to clipboard.
const copyImageToClipBoardSafari = () => {
if(isSafari) {
navigator.clipboard
.write([
new ClipboardItem({
"image/png": new Promise(async (resolve, reject) => {
try {
const blob = await snapshotCreator();
resolve(new Blob([blob], { type: "image/png" }));
} catch (err) {
reject(err);
}
}),
}),
])
.then(() =>
// Success
)
.catch((err) =>
// Error
console.error("Error:", err)
);
}
}
We're using navigation.clipboard.write
property
to write an image blob to clipboard. Inside it,
we're creating a new ClipboardItem from the blob
of the element which is generated from snapshotCreator()
function which we created in first step.
Since this method doesn't work in Firefox, we'll need a check to make sure we're not running it on Firefox. Below condition takes care of that.
const isNotFirefox = navigator.userAgent.indexOf("Firefox") < 0;
Now that we have the check, we'll use
the same technique as we used for Safari,
but this time we'll need to ask the browser
for the navigator
permissions.
Below is the function that does that and then copies the blob image to clipboard in Chrome browser and its variants.
const copyImageToClipBoardOtherBrowsers = () => {
if(isNotFirefox) {
navigator?.permissions
?.query({ name: "clipboard-write" })
.then(async (result) => {
if (result.state === "granted") {
const type = "image/png";
const blob = await snapshotCreator();
let data = [new ClipboardItem({ [type]: blob })];
navigator.clipboard
.write(data)
.then(() => {
// Success
})
.catch((err) => {
// Error
console.error("Error:", err)
});
}
});
} else {
alert("Firefox does not support this functionality");
}
}
The difference is not much except that
Chrome and its variants require asking for
navigator.permissions
before we can
write the blob content to clipboard.
The above function uses the same snapshotCreator()
function from the firs step to
create an image from the element.
We can combine both the functions together to have this functionality work in Safari and Chrome browser. You can check how it works on this page, just click on "Copy Image" button and then you'll be able paste the image anywhere.
If this blog was helpful, check out our full blog archive.