Core clipboard utilities for the @ngockhoi96/ctc library. Zero runtime dependencies, tree-shakeable, SSR-safe, framework-agnostic.
All functions are SSR-safe -- safe to import in Next.js, Nuxt, or any server-side environment without crashing.
# npm
npm install @ngockhoi96/ctc
# pnpm
pnpm add @ngockhoi96/ctc
# bun
bun add @ngockhoi96/ctcimport { copyToClipboard, isClipboardSupported } from '@ngockhoi96/ctc/clipboard'
button.addEventListener('click', async () => {
if (isClipboardSupported()) {
const success = await copyToClipboard('Hello, world!')
console.log(success ? 'Copied!' : 'Copy failed')
}
})Copy text to the clipboard using the modern Clipboard API.
function copyToClipboard(text: string, options?: ClipboardOptions): Promise<boolean>Returns: true on success, false on any failure (never throws).
Browser support: Requires HTTPS (secure context) and a user gesture (click, keydown, etc.). Chrome 66+, Firefox 63+, Safari 13.1+.
import { copyToClipboard } from '@ngockhoi96/ctc/clipboard'
button.addEventListener('click', async () => {
const success = await copyToClipboard('Hello, world!')
if (!success) {
showError('Copy failed -- check HTTPS and permissions')
}
})Remarks:
- Must be called synchronously within a user gesture handler. Programmatic calls from timers or microtasks will be rejected by the browser with
CLIPBOARD_PERMISSION_DENIED. - Returns
falseon HTTP pages withINSECURE_CONTEXTerror code. UsecopyToClipboardLegacy()for HTTP environments. - On Safari, calling any async operation before
writeText()in the same microtask may break the user activation window. Keep the call synchronous within the click handler.
Read text from the clipboard using the modern Clipboard API.
function readFromClipboard(options?: ClipboardOptions): Promise<string | null>Returns: The clipboard text string on success, null on failure (never throws).
Browser support: Requires HTTPS (secure context) and a user gesture. May trigger a permission prompt on first call. Chrome 66+, Firefox 63+, Safari 13.1+.
import { readFromClipboard } from '@ngockhoi96/ctc/clipboard'
pasteButton.addEventListener('click', async () => {
const text = await readFromClipboard()
if (text !== null) {
input.value = text
}
})Remarks:
- Chrome prompts for
clipboard-readpermission on the first call. Firefox and Safari show a system-level paste prompt. - If the clipboard contains only non-text content (e.g., an image), the read operation fails with
CLIPBOARD_READ_FAILED.
Check if the Clipboard API is available and usable for write operations in the current context.
function isClipboardSupported(): booleanReturns: true if clipboard write operations are supported. Returns false in SSR environments, on HTTP pages, or when the Clipboard API is absent.
Browser support: Synchronous check, no permissions needed. Chrome 66+, Firefox 63+, Safari 13.1+. Returns false on HTTP and in SSR.
import { isClipboardSupported, copyToClipboardLegacy, copyToClipboard } from '@ngockhoi96/ctc/clipboard'
if (isClipboardSupported()) {
await copyToClipboard(text)
} else {
copyToClipboardLegacy(text)
}Remarks:
- Permission state is not checked. A
trueresult does not guarantee the user has granted clipboard access. Permission denial is surfaced at call time viaCLIPBOARD_PERMISSION_DENIED.
Check if clipboard read operations are available and usable in the current context.
function isClipboardReadSupported(): booleanReturns: true if clipboard read operations are supported. Returns false in SSR environments, on HTTP pages, or when the read API is absent.
Browser support: Synchronous check, no permissions needed. Chrome 66+, Firefox 63+, Safari 13.1+. Returns false on HTTP and in SSR.
import { isClipboardReadSupported, readFromClipboard } from '@ngockhoi96/ctc/clipboard'
if (isClipboardReadSupported()) {
const text = await readFromClipboard()
}Remarks:
- Firefox does not support the Permissions API
clipboard-readquery. This function uses synchronous feature detection only.
Copy text to the clipboard using the deprecated document.execCommand('copy') API.
function copyToClipboardLegacy(text: string, options?: ClipboardOptions): booleanReturns: true on success, false on failure (synchronous, never throws).
Browser support: Works on HTTP pages via deprecated execCommand. Supported in all desktop browsers. Not reliable on iOS Safari. Use only when the modern Clipboard API is unavailable.
import { isClipboardSupported, copyToClipboard, copyToClipboardLegacy } from '@ngockhoi96/ctc/clipboard'
async function copyText(text: string): Promise<boolean> {
if (isClipboardSupported()) {
return copyToClipboard(text)
}
return copyToClipboardLegacy(text)
}Remarks:
- Uses the deprecated
document.execCommand('copy')API, which is synchronous and text-only. - No secure context requirement. This function exists for environments where
copyToClipboard()is unavailable due to HTTP. - Temporarily creates and removes a textarea element in the DOM to perform the copy operation.
execCommandcopy is not reliably supported on iOS Safari. This function may returnfalseon iOS Safari without a usable fallback.
All clipboard functions accept an optional onError callback that receives a structured BrowserUtilsError object. Functions never throw -- they return false or null on failure.
interface BrowserUtilsError {
code: ErrorCode
message: string
cause?: unknown
}
type ErrorCode =
| 'CLIPBOARD_NOT_SUPPORTED'
| 'CLIPBOARD_PERMISSION_DENIED'
| 'CLIPBOARD_WRITE_FAILED'
| 'CLIPBOARD_READ_FAILED'
| 'INSECURE_CONTEXT'interface ClipboardOptions {
onError?: (error: BrowserUtilsError) => void
}| Code | Description |
|---|---|
CLIPBOARD_NOT_SUPPORTED |
Clipboard API is unavailable (SSR environment, very old browser, or navigator.clipboard missing) |
INSECURE_CONTEXT |
Page is served over HTTP (not HTTPS). The Clipboard API requires a secure context. Use copyToClipboardLegacy() instead |
CLIPBOARD_PERMISSION_DENIED |
User denied clipboard permission, or the call was made outside a user gesture |
CLIPBOARD_WRITE_FAILED |
Write operation failed for an unexpected reason |
CLIPBOARD_READ_FAILED |
Read operation failed for an unexpected reason |
import { copyToClipboard } from '@ngockhoi96/ctc/clipboard'
await copyToClipboard('text', {
onError: (err) => {
switch (err.code) {
case 'CLIPBOARD_PERMISSION_DENIED':
showPermissionPrompt()
break
case 'INSECURE_CONTEXT':
// Fall back to legacy method on HTTP pages
copyToClipboardLegacy(text)
break
default:
console.error('Clipboard error:', err.message)
}
}
})Without onError, expected failures (CLIPBOARD_NOT_SUPPORTED, INSECURE_CONTEXT, CLIPBOARD_PERMISSION_DENIED) are logged with console.warn. Unexpected failures (CLIPBOARD_WRITE_FAILED, CLIPBOARD_READ_FAILED) are logged with console.error.
See the browser support table in the root README. All functions require ES2020+ (>95% global browser support) and are SSR-safe.