FairGarden Docs

Use Copier

The useCopier hook provides robust clipboard copy functionality with success state management, error handling, and customizable callbacks. It's designed for code blocks, buttons, and interactive elements that need copy-to-clipboard functionality.

Note

When using CodeHighlighter, the useCode and useDemo hooks already provide built-in copy functionality, so useCopier is not needed.

Basic Usage

Text Input Copy

A simple example showing how to copy text from an input field.

'use client';
import * as React from 'react';
import { useCopier } from '@fairgarden/docs/useCopier';
import styles from './TextInputCopy.module.css';

export function TextInputCopy() {
  const [text, setText] = React.useState('Hello, copy me!');
  const { copy, recentlySuccessful } = useCopier(() => text);

  return (
    <div className={styles.container}>
      <input
        type="text"
        value={text}
        onChange={(event) => setText(event.target.value)}
        className={styles.input}
        placeholder="Enter text to copy…"
        aria-label="Text to copy"
      />
      <button type="button" onClick={copy} className={styles.button}>
        {recentlySuccessful ? '✓ Copied!' : 'Copy'}
      </button>
    </div>
  );
}

Advanced Usage

Dynamic Content

import { useCopier } from '@fairgarden/docs/useCopier';

function CodeBlockCopy({ getCode }: { getCode: () => string }) {
  const { copy, recentlySuccessful } = useCopier(getCode);

  return (
    <button onClick={copy} disabled={recentlySuccessful}>
      {recentlySuccessful ? '✓ Copied' : 'Copy Code'}
    </button>
  );
}

With Error Handling

import { useCopier } from '@fairgarden/docs/useCopier';

function CopyWithFeedback({ content }: { content: string }) {
  const [error, setError] = React.useState<string | null>(null);

  const { copy, recentlySuccessful } = useCopier(content, {
    onCopied: () => setError(null),
    onError: (err) => setError('Failed to copy to clipboard'),
    timeout: 3000,
  });

  return (
    <div>
      <button onClick={copy}>{recentlySuccessful ? 'Copied!' : 'Copy'}</button>
      {error && <span style={{ color: 'red' }}>{error}</span>}
    </div>
  );
}

With Custom Analytics

import { useCopier } from '@fairgarden/docs/useCopier';

function AnalyticsCopyButton({ text, label }: { text: string; label: string }) {
  const { copy, recentlySuccessful } = useCopier(text, {
    onCopied: () => {
      // Track successful copy events
      analytics.track('Code Copied', { label });
    },
    onError: (error) => {
      // Track copy failures
      analytics.track('Copy Failed', { label, error: String(error) });
    },
    onClick: (event) => {
      // Track all copy attempts
      analytics.track('Copy Attempted', { label });
    },
  });

  return (
    <button onClick={copy} className={recentlySuccessful ? 'success' : ''}>
      {recentlySuccessful ? 'Copied!' : `Copy ${label}`}
    </button>
  );
}

Types

ParameterTypeDescription
contents
(() => string | undefined) | string
opts
UseCopierOpts | undefined
Return Type
KeyTypeRequired
copy
(event: React.MouseEvent) => Promise<void>
Yes
recentlySuccessful
boolean
Yes
UseCopierOpts
type UseCopierOpts = {
  onCopied?: OnCopied;
  onError?: OnCopyError;
  onClick?: OnCopyClick;
  timeout?: number;
}

How It Works

  1. Content Resolution: When copy is called, resolves content from string or function
  2. Clipboard API: Uses the clipboard-copy library for cross-browser compatibility
  3. Success State: Sets recentlySuccessful to true after successful copy
  4. Timeout Management: Automatically resets success state after configured timeout
  5. Cleanup: Clears the timeout on unmount, and a write that finishes after unmounting starts none

State Management

The hook manages internal state for copy feedback:

  • Initial state: recentlySuccessful is false
  • On success: State becomes true and the timeout countdown (re)starts
  • After timeout: State resets to false
  • Copying again: State stays true while the new write is in flight, so feedback keyed on it doesn't flicker; each successful write restarts the countdown, so the state stays true until timeout after the last write to finish. A write still in flight timeout after it started ends the feedback, so a stalled write can't keep it on. Use onCopied to react to every copy.
  • On error: State becomes false and the error callback is fired
  • On empty content: Nothing is written and state becomes false; neither onCopied nor onError is fired, but onClick still is

Error Handling

Copy operations can fail for various reasons:

  • Security restrictions: Some browsers require user interaction
  • Permission denied: Clipboard access may be blocked

The hook provides the onError callback to handle these cases gracefully.

Empty content (an empty string, or a function that returns undefined) is not an error: there is nothing to copy, so the clipboard is left untouched and recentlySuccessful stays false.


When to Use

  • Code blocks - Copy source code, commands, or configurations
  • Documentation - Copy API endpoints, package names, or examples
  • Interactive demos - Copy generated output or current state
  • Forms - Copy generated URLs, tokens, or formatted data
  • Any UI element - That benefits from copy-to-clipboard functionality

Browser Support

Uses the clipboard-copy library which provides:

  • Modern navigator.clipboard.writeText() API when available
  • Fallback to document.execCommand('copy') for older browsers
  • Works in both secure (HTTPS) and insecure contexts