Radzion
2 min readJul 1, 2023

Watch on YouTube | 🐙 GitHub | 🎮 Demo

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 ReactKit

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.

Radzion
Radzion

Written by Radzion

Crafting increaser.org to turn your goals into reality.

No responses yet