Creating a Copy to Clipboard Button in React
4 min read
I'm currently working on a social sharing module that I can place at the bottom of my React app screens. The idea is to allow visitors to share my link on their socials with a quick click of a social icon. In addition to the usual suspects of Facebook, Twitter and Instagram, I also wanted to include a "copy to clipboard" option for visitors that didn't necessarily want to post directly on a social platform. Here's a step by step on how I created a...
Copy to clipboard button
My end goal was to have a component comprising an icon and text that reads "Copy link". Upon clicking on the component, the text should change to "Copied" and blink for a couple of seconds, before reverting back to "Copy link".
Step 1 - Javascript logic
I had no idea how to effect a copy to clipboard action in Javascript before starting this build. Turns out it's not that hard, but you have to do it in a slightly roundabout way. Essentially, what's happening is that you need to:
- Create an
input
element in the DOM and set a value for it; - Append the
input
element to the document body, which then allows you to select it; - Run the
copy
command so that the value is now copied to clipboard; and - Remove this temporary
input
element you've just been created, from the DOM.
function copyToClipboard() {
const tempInput = document.createElement('input')
tempInput.value = 'https://bionicjulia.com/examplelink'
document.body.appendChild(tempInput)
tempInput.select()
document.execCommand('copy')
document.body.removeChild(tempInput)
}
Step 2 - Create the React component
The next step is to bundle this logic up into a React component and hook it to an onClick
event. I've called the function copyToClipboard
.
One thing to note is that I added a conditional check to first assess whether a user's browser even supports the copy
command in the first place. This is done with the document.queryCommandSupported('copy')
statement. If it doesn't, this entire component would not work and thus should not render anything visible.
export const CopyTextIcon: React.FC = () => {
function copyToClipboard() {
const tempInput = document.createElement('input')
tempInput.value = 'https://bionicjulia.com/examplelink'
document.body.appendChild(tempInput)
tempInput.select()
document.execCommand('copy')
document.body.removeChild(tempInput)
}
return (
<>
{document.queryCommandSupported('copy') && (
<div id="copy-icon" onClick={() => copyToClipboard()} className="cursor-pointer">
<div className="mt-8 flex justify-center">
<img src={`${appConfig.publicUrl}/img/social/copy-mobile.svg`} alt="Copy" />
<p className="ml-2 text-base-secondary text-sm light">Copy link</p>
</div>
</div>
)}
</>
)
}
Step 3 - Manage the copy text state
The next step was to manage the state of the "Copy link" text. To do this in React, I used the useState
hook and coupled this with a timeout function within a useEffect
hook.
const [copySuccess, setCopySuccess] = useState('')
useEffect(() => {
setTimeout(() => setCopySuccess(''), 2000)
}, [copySuccess])
The other key step is to setCopySuccess
state to 'Copied'
upon the onClick
event.
function copyToClipboard() {
const tempInput = document.createElement('input')
tempInput.value = 'https://bionicjulia.com/examplelink'
document.body.appendChild(tempInput)
tempInput.select()
document.execCommand('copy')
document.body.removeChild(tempInput)
setCopySuccess('Copied')
}
To summarise what's happening here:
- The initial state of
copySuccess
is a blank string''
; - Upon clicking the button, the
copySuccess
state is set to'Copied'
; - The
useEffect
hook kicks in and resets thecopySuccess
state to a blank string''
after 2 seconds.
Step 4: Animate the text
Finally, we hook up the "Copy link" text and make it dependent on the copySuccess
state, and then add some blinking animation (I've called the class blink
).
return (
<>
{document.queryCommandSupported('copy') && (
<div id="copy-icon" onClick={() => copyToClipboard()} className="cursor-pointer">
<div className="mt-8 flex justify-center">
<img src={`${appConfig.publicUrl}/img/social/copy-mobile.svg`} alt="Copy" />
<p className="ml-2 text-base-secondary text-sm light">
{copySuccess ? <span className="blink">{copySuccess}</span> : 'Copy link'}
</p>
</div>
</div>
)}
</>
)
Here's the simple CSS animation I created for the class.
.blink {
animation: blinker 1s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0.2;
}
}
And that's it! No need for any external libraries. 🤓