How to add a ‘copy to clipboard’ button to code blocks
If you are writing about code, you are likely to include some code blocks to complement what it is you are discussing. To improve the experience for the reader, you can consider adding a “copy code to clipboard” button to make it simple to copy and paste the code - a key developer skill after all!
I will show you how to use the Clipboard API that enables you to asynchronously write to the system clipboard. I will show how you can position and style the button in a code block. Also, I will show you how you can provide feedback to the user that the code was in fact copied!
A blog example with a copy button on every code block
We only want to add our button to code blocks that are wrapped inside a pre
such as below:
In this example, I will use the syntax highlighting library Prism on the client-side. Prism is quite popular and is used in the static site generator Eleventy at build time. Since there are many syntax highlighting libaries, I will cover a simple, general solution.
According to the HTML5 spec, the recommended way to define the language for a code block is a through adding a class in the form of <language-xxxx>
where xxxx is the language name i.e. a class of language-css
for a CSS code block as per example above.
Prism looks for all code
elements that have a <language-xxxx>
class to identify blocks to add highlighting to. Prism copies the <language-xxxx>
class from the code
element to the pre
for you. This will make it easier for you to target the correct elements in your CSS later.
Here is the codepen of what I will cover below.
CSS code
I want to position the button in the top right corner of the pre
. To achieve this placement, we set the pre
as position: relative
and the button as position: absolute
, and set the top
and right
properties of the button.
Also, we want to add enough top padding to the pre
to make space for the button, so that the text will never be covered by the button. On small screens this may happen.
You can place the button somewhere else, and style it differently if you prefer.
Some people like to hide the button and only show it when you hover over the code block. I think that is bad for user experience. You shouldn’t need to uncover this type of functionality!
JavaScript code
We only want to target the pre
elements that contain a code
element. If you are using a static site generator that does the syntax highlighting at build time - usually it adds a class to the pre
- you can use that to reference it.
If you don’t have a class available, it is a bit tricker to target the code blocks since a query selection along the lines of “find me all pre elements that contain a code element” is not possible with querySelectorAll()
, because currently there is no parent selector available in CSS syntax (the :has()
psuedo-class is coming soon though). In that event, you would need to write an extra bit of JavaScript code to reference the correct elements.
Writing to the system clipboard is quite straightforward. There is a browser API, the Clipboard API, which enables you to asynchronously read from and write to the system clipboard. The browser support is excellent (for writing to the clipboard). It is recommended that you use the Clipboard API instead of the deprecated document.execCommand()
method.
To access the clipboard, you use the navigator.clipboard
global. To write to the clipboard there is an async writeText()
function.
In the event handler for the button, we want to get the text of the code
element and copy it to the clipbboard. We will name our event handler copyCode
and make it async
because of the call to the async writeText
function.
We can pass the pre
block element as a parameter to get the text of the child code
element. In the handler function, we get a reference to the child code
element by let code = block.querySelector("code")
, and then we get its text through its innerText
property.
Putting this all together, the code looks like this:
Warning: If you use a client-side syntax highlighting library and rely on it adding a class to the pre
, keep in mind that you need to wait until that is finished first before you run the above code. It is preferable to avoid this scenario altogether.
While the above code works perfectly well, we should check if it is accessible to keyboard user. And also, it would be nice to give some visual feedback to indicate to the user that the task was completed successfully.
Accessibility
One thing that is often overlooked is accessibility! Code blocks are not focusable by a keyboard by default. This means that if your audience is using only a keyboard to navigate the site, they will be unable to access the content that has overflowed the container, which would usually be scrolled with a mouse.
Prism adds an attribute to the pre
to make it make a tab stop - it adds the attribute-value pair of tabindex=0
. So, if you hit the Tab key, it will focus on the code block. You can then you use the right arrow key to scroll to the right.
Not every syntax highligthing library will do this for you, so check to be sure!
If this is not done for you, you have 2 options to fix it:
-
In CSS, you could prevent overflow from happening by forcing text to break onto the next line. The following rule will achieve that.
-
In JavaScript, you can add
tabindex
attribute to thepre
element, and give it a value of zero to make it focusable.
Adding feedback for completion of task
The first thing that popped into my head was to change the button text to “Code Copied” when the action is done, and reset it to “Copy Code” after a small delay. You can see a GIF of this in action below.
The JavaScript is short. Below I reset the text after 700 milliseconds through setTimeout()
. This time seems adequate to me. The duration is arbitary, you can choose a different duration if you like!
We add this code to our copyCode
event handler. Also, we need to add the button
as a parameter to be able to change its text.
If you don’t like the fact that the button grows in size when the text is switched, you can set a min-width
on the button. You can set the min-width
to the width of the button when it has the text “Code Copied”. This would be min-width: 6.5rem;
in my case.
If you want to provide feedback in another way, you could show a toast notification or create an animation of some sort.