Highlight.js is quick and easy tool to add syntax highlighting to your code blocks but one feature it lacks is a copy button to easily copy the contents of the code block.
I use Highlight.js on this website and wanted this feature to be able to quickly copy things I re-use like this regex or this env.ts file whenever I start a new project so I looked at their Plugin API and was pleasantly surprised at how easy it was to extend.
The snippet below is all you need to add a copy button to your code blocks highlighted with Highlight.js and you can test the button while copying it. 😄
hljs.addPlugin({
"after:highlightElement": ({ el, text }) => {
/**
* el is the <code> element that was highlighted
* el.parentElement is the <pre> element
*/
const wrapper = el.parentElement;
if (wrapper == null) {
return;
}
/**
* Make the parent relative so we can absolutely
* position the copy button
*/
wrapper.classList.add("relative");
const copyButton = document.createElement("button");
copyButton.classList.add(
"absolute",
"top-2",
"right-2",
"p-2",
"text-gray-500",
"hover:text-gray-700",
);
// Lucide copy icon
copyButton.innerHTML = `<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`;
copyButton.onclick = () => {
navigator.clipboard.writeText(text);
// Notify user that the content has been copied
toast.success("Copied to clipboard", {
description: "The code block content has been copied to the clipboard.",
});
};
// Append the copy button to the wrapper
wrapper.appendChild(copyButton);
},
});