Copy Text to Clipboard on Click React Component

Copy Text to Clipboard on Click React Component

July 1, 2023

2 min read

Copy Text to Clipboard on Click React Component
Watch on YouTube

Let's make a beautiful React component that will copy text to the clipboard on click. It shows a copy icon at the end to signify that you can interact with the text, it becomes brighter when hovered, and on click, the copy icon turns into a check icon.

Example from RadzionKit
Example from RadzionKit

The CopyText component receives the same properties as the Text component plus content property that should contain the text to copy. To learn more about the reusable Text component, check out this post.

import { Text } from "lib/ui/Text"
import { CopyIcon } from "./icons/CopyIcon"
import styled from "styled-components"
import { getColor } from "./theme/getters"
import copy from "copy-to-clipboard"
import { defaultTransitionCSS } from "./animations/transitions"
import { useState } from "react"
import { Match } from "./Match"
import { CheckIcon } from "./icons/CheckIcon"

interface CopyTextProps extends React.ComponentProps<typeof Text> {
  content: string
}

const IconWr = styled(Text)`
  margin-left: 4px;
  ${defaultTransitionCSS};
  color: ${getColor("textSupporting3")};
`

const Container = styled(Text)`
  cursor: pointer;

  &:hover ${IconWr} {
    color: ${getColor("contrast")};
  }
`

type IconToShow = "copy" | "copied"

export const CopyText = ({ content, children, ...rest }: CopyTextProps) => {
  const [iconToShow, setIconToShow] = useState<IconToShow>("copy")

  return (
    <Container
      onMouseLeave={() => setIconToShow("copy")}
      onTouchEnd={() => setIconToShow("copy")}
      onClick={() => {
        copy(content)
        setIconToShow("copied")
      }}
      {...rest}
    >
      {children}
      <IconWr as="span">
        <Match
          value={iconToShow}
          copy={() => <CopyIcon />}
          copied={() => <CheckIcon />}
        />
      </IconWr>
    </Container>
  )
}

The Container component modifies styles of the Text component by adding pointer cursor together with a hover effect that will change color of the IconWr component. Inside of the Container we place the children and the IconWr component that will show the copy icon or the check icon depending on the iconToShow state. The Match component is a simple helper that will render the content based on the value property. It is a better alternative to the switch statement.

import { ReactNode } from "react"

type MatchProps<T extends string | number | symbol> = Record<
  T,
  () => ReactNode
> & {
  value: T
}

export function Match<T extends string | number | symbol>(
  props: MatchProps<T>
) {
  const render = props[props.value]

  return <>{render()}</>
}

For a better user experience we change the copy icon to the check icon on click and reset it back to the copy icon when user leaves the mouse on removes finger from the touch screen.