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;
}

Links