Clipboard Copy
Create a component with a text box and a button that copies the text in the box to the clipboard when clicked.
Solution
Video
Explanation
The JS Clipboard API
To be able to write to the clipboard, you'll notice we use the line, navigator.clipboard.writeText(copyText)
in the code.
How to copy the text
Link the textArea
element with a ref
. Now in the copyToClipboard
function you can see that the value is checked and assigned.
The Clipboard API is asynchronous, so copyToClipboard
is async
.
Adding User Interactions
Alone, there would be no clear way to know that you had copied to clipboard until you went to use it. This isn't ideal so we'll add some interactions to improve it.
Track button click and text
Using useState
we'll assign an intial text of 'Copy to Clipboard'. We will also track whether the button is idle or in a state of 'copying'.
The function, buttonFeedback
, updates the text when the button is clicked and sets the state of the button to being active, setting off new styles.
Reset effect
To get the effect of the button resetting to it's original state, a setTimeout
function is used.
Code
import { useRef, useState } from "react";
import styles from "./Solution.module.css";
const Solution = () => {
const initialButtonText = "Copy to Clipboard";
const [buttonIdle, setButtonIdle] = useState(true);
const [buttonText, setButtonText] = useState<string>(initialButtonText);
const buttonFeedback = () => {
setButtonText("Copied!");
setButtonIdle(false);
// return text and button after half a second
setTimeout(() => {
setButtonText(initialButtonText);
setButtonIdle(true);
}, 500);
};
const textRef = useRef<null | HTMLTextAreaElement>(null);
const clickHandler = () => {
copyToClipboard();
buttonFeedback();
};
const copyToClipboard = async (): Promise<void> => {
const copyText = textRef?.current?.value;
if (copyText) {
try {
await navigator.clipboard.writeText(copyText);
// make the button say 'copied' when complete
} catch (error) {
console.error(error);
}
}
};
return (
<div>
<textarea
ref={textRef}
cols={50}
rows={5}
placeholder="Enter your message to be copied here"
className={styles.textArea}
/>
<button
onClick={clickHandler}
className={`${styles.button} ${
buttonIdle ? styles.idle : styles.active
}`}
>
{buttonText}
</button>
<textarea
cols={50}
rows={5}
placeholder="Test that you can paste it when you're done!"
className={styles.textArea}
/>
</div>
);
};
export default Solution;
Styling
.button {
display: block;
padding: 10px 20px;
width: 200px;
cursor: pointer;
margin: 5px 0 50px;
transition: all 0.3s ease-out;
}
.idle {
transform: scale(1);
background-color: #e9e9e9;
}
.active {
transform: scale(1.05);
background-color: white;
}
.textArea {
padding: 5px;
}