✨ 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.
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.
Top comments (3)
if you are looking for custom solution in Vanilla JS check this article:
dirask.com/posts/JavaScript-copy-t...
There are available 2 versions: modern and legacy.
Or, if you want more than text, you want styles too, then, consider:
github.com/bernd-wechner/Copy-with...
If you want to use a third-party library, then go for 'react-use,' which has a 'useCopyToClipboard' hook that is easy to use.