useCopy (React)

Copy text to your clipboard, and know when it's done so you can change the button's text to copied!.

typescriptjavascript
import { useState, useCallback, useRef } from 'react'
 
export type UseCopyProps = {
  copiedTimeout?: number
}
 
type CopyFn = (text: string) => Promise<void>
 
export const useCopy = ({ copiedTimeout = 2000 }: UseCopyProps = {}): [
  copy: CopyFn,
  copied: boolean
] => {
  const [copied, setCopied] = useState(false)
  const timeoutRef = useRef<ReturnType<typeof setTimeout>>()
 
  const copy: CopyFn = useCallback(
    async text => {
      await navigator.clipboard.writeText(text)
      setCopied(true)
      if (timeoutRef.current) clearTimeout(timeoutRef.current)
      timeoutRef.current = setTimeout(() => setCopied(false), copiedTimeout)
    },
    [copiedTimeout]
  )
 
  return [copy, copied]
}
import { useState, useCallback, useRef } from 'react'
 
export const useCopy = ({ copiedTimeout = 2000 } = {}) => {
  const [copied, setCopied] = useState(false)
  const timeoutRef = useRef()
 
  const copy = useCallback(
    async text => {
      await navigator.clipboard.writeText(text)
      setCopied(true)
      if (timeoutRef.current) clearTimeout(timeoutRef.current)
      timeoutRef.current = setTimeout(() => setCopied(false), copiedTimeout)
    },
    [copiedTimeout]
  )
 
  return [copy, copied]
}

Info

  • We use useCallback, so that if you follow hook rules and use the copy function inside a useEffect or another useCallback, you won't have code (re-)running unnecessarily.

useCopy implemented with useTemporaryState

typescriptjavascript
export type UseCopyProps = {
  copiedTimeout?: number
}
 
type CopyFn = (text: string) => Promise<void>
 
export const useCopy = ({ copiedTimeout = 2000 }: UseCopyProps = {}): [
  copy: CopyFn,
  copied: boolean
] => {
  const [copied, setCopied] = useTemporaryState(false, copiedTimeout)
 
  const copy: CopyFn = useCallback(
    async text => {
      await navigator.clipboard.writeText(text)
      setCopied(true)
    },
    [setCopied]
  )
 
  return [copy, copied]
}
export const useCopy = ({ copiedTimeout = 2000 } = {}) => {
  const [copied, setCopied] = useTemporaryState(false, copiedTimeout)
 
  const copy = useCallback(
    async text => {
      await navigator.clipboard.writeText(text)
      setCopied(true)
    },
    [setCopied]
  )
 
  return [copy, copied]
}

Check out the SolidJS version here.

Created 02/05/22Updated 19/06/23
Found a mistake, or want to suggest an improvement? Source on GitHub here
and see edit history here