Recently I was creating a new repo on GitHub, a pretty common action for most of us now-a-days. When I noticed a feature which I use everytime but had never given much thought to, the copy to clipboard button.
This is a really useful feature, as I said a second ago, I literally use it everytime. Another great example of this can be found on the Bootstrap site. Each code example has a copy button in the top right corner allowing developers to copy the sample code straight to their clipboard.
I thought this would be a cool little feature to be able to use in Blazor applications so thought I would do a bit of investigation and see how it could be replicated.
In this post I’m going to show you how to create a simple copy to clipboard feature for Blazor apps. We’re going to start by looking at the available APIs we can use for this functionality. From there we are going to create two solutions, one for short amounts of text, replicating the GitHub example above. The other for larger amounts of text, replicating the functionality from the Bootstrap docs.
Choosing the API
The first thing to understand is that we can’t create the feature using C# code alone, we’re going to have to use some JavaScript interop to achieve our goal. There are two API options available to us, Document.execCommand
and Clipboard
.
Document.execCommand
Historically, clipboard operations have been achieved using execCommand
. A quick google of “copy to clipboard in JavaScript” will bring up numerous examples using this API. execCommand
is also well supported across the different browsers, a quick check on caniuse.com shows lots of green (95.63%).
However, there is a rather large issue with this API. It’s been marked obsolete.
The good news is there’s a new API which supersedes it called the Clipboard API.
Clipboard API
The new Clipboard API has the ability to read and write to the clipboard both syncronously and asyncronously, as well as integrating with the Permissions API to ensure the user has given permission to do so.
The API breaks down into 2 interfaces, [Clipboard](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard)
and [ClipboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent)
. The ClipboardEvent
interface gives us access to information about the modification of the clipboard by events such as cut, copy and paste. It’s good to know this is here but the more intestesting stuff is in the Clipboard
interface.
The Clipboard
interface provides us the functions to interact with the clipboard in our applications and contains the following 4 functions (from the MDN docs):
- **
read()
**Requests arbitrary data (such as images) from the clipboard, returning aPromise
. When the data has been retrieved, the promise is resolved with aDataTransfer
object that provides the data. - **
readText()
**Requests text from the system clipboard; returns aPromise
which is resolved with aDOMString
containing the clipboard’s text once it’s available. - **
write()
**Writes arbitrary data to the system clipboard. This asynchronous operation signals that it’s finished by resolving the returnedPromise
. - **
writeText()
**Writes text to the system clipboard, returning aPromise
which is resolved once the text is fully copied into the clipboard.
The adoption of this new API isn’t anywhere near as widespread as the old execCommand
. The function we’re interested in is writeText
has 71.11% adoption according to caniuse.
However, the browsers that don’t support this also don’t support Blazor, so that makes things simple. Based on all the information I decided to go with the new clipboard API for this functionality.
Solution 1: Replicating GitHubs copy to clipboard
In this first solution we’re going to replicate the funcationality from GitHub. This is ideal for any small, single line amounts of text you want to allow users to copy to their clipboards.
First create a component call CopyToClipboard
with the following code.
Note: I’m doing this using the standard Blazor project template which has Bootstrap included. So for styling, I’m just using classes from that and some inline styles where needed.
@inject IJSRuntime JSRuntime
<div class="form-inline">
<input class="form-control" readonly type="text" value="@Text" />
<button type="button" class="btn btn-primary" @onclick="CopyTextToClipboard">Copy</button>
</div>
@code {
[Parameter] public string Text { get; set; }
private async Task CopyTextToClipboard()
{
await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", Text);
}
}
The component takes in the text which can be copied by the user via the Text
parameter. When the user click on the button
the CopyTextToClipboard
is invoked which calls the following JavaScript.
window.clipboardCopy = {
copyText: function(text) {
navigator.clipboard.writeText(text).then(function () {
alert("Copied to clipboard!");
})
.catch(function (error) {
alert(error);
});
}
};
The above function calls writeText
to write the text provided from the CopyToClipboard
component to the users clipboard.
We can use the component like this.
<CopyToClipboard Text="Copy this text" />
Which will produce the following output.
Solution 2: Replicating Bootstraps copy to clipboard
This time we’re going to replicate the functionality from Bootstrap docs. This is great for copying larger amounts of text. Here is the updated code for the CopyToClipboard
component.
@inject IJSRuntime JSRuntime
<div class="position-relative" style="background-color: #f5f5f5">
<pre>
<code @ref="_codeElement">
@ChildContent
</code>
</pre>
<div style="position:absolute; top: 10px; right: 10px;">
<button type="button" class="btn btn-primary" @onclick="CopyTextToClipboard">Copy</button>
</div>
</div>
@code {
private ElementReference _codeElement;
[Parameter] public RenderFragment ChildContent { get; set; }
private async Task CopyTextToClipboard()
{
await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", _codeElement);
}
}
This time we’re taking in child content defined by the components consumer and rendering it inside a code
tag. We’re capturing a reference to that element using Blazors @ref
directive and passing that to JS when the copy button is clicked.
window.clipboardCopy = {
copyText: function (codeElement) {
navigator.clipboard.writeText(codeElement.textContent).then(function () {
alert("Copied to clipboard!");
})
.catch(function (error) {
alert(error);
});
}
}
The JavaScript code is largly the same as before. The only difference is we’re receiving an HTML element instead of a text string. When we call writeText
we’re now passing in the text inside the code
element using the textContent
property.
We can use component like this.
<CopyToClipboard>
@("<div class=\"clipboard-copy\">")
@("<button type=\"button\" class=\"btn btn-primary\">Copy</button>")
@("</div>")
</CopyToClipboard>
Which will produce the following output.
Summary
In this post I’ve show a couple of solutions for copying text to the users clipboard in Blazor. We started off by understanding the two API’s available in JavaScript for interacting with the clipboard, execCommand
and the Clipboard
API. We concluded that using the new Clipboard API was a better choice due to the execCommand
API being marked obsolete.
We then looked at two solutions for implementing copy to clipboard functionality. The first allowed short string to be copied to the users clipboard via a simple component with a button which invoked a call into JavaScript. The second method showed how to copy larger volumes of text by passing an ElementReference
to JavaScript and accessing its textContent
property to retrieve the text before copying it to the clipboard.