npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ngockhoi96/ctc

v0.3.0

Published

Zero-dependency clipboard utilities for modern browsers — copy, read, and detect support. SSR-safe, TypeScript-native, tree-shakeable.

Readme

📋 @ngockhoi96/ctc

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.

📦 Install

# npm
npm install @ngockhoi96/ctc

# pnpm
pnpm add @ngockhoi96/ctc

# bun
bun add @ngockhoi96/ctc

Quick Start

import { copyToClipboard, isClipboardSupported } from '@ngockhoi96/ctc/clipboard'

button.addEventListener('click', async () => {
  if (isClipboardSupported()) {
    const success = await copyToClipboard('Hello, world!')
    console.log(success ? 'Copied!' : 'Copy failed')
  }
})

API Reference

copyToClipboard(text, options?)

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 false on HTTP pages with INSECURE_CONTEXT error code. Use copyToClipboardLegacy() 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.

readFromClipboard(options?)

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-read permission 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.

isClipboardSupported()

Check if the Clipboard API is available and usable for write operations in the current context.

function isClipboardSupported(): boolean

Returns: 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 true result does not guarantee the user has granted clipboard access. Permission denial is surfaced at call time via CLIPBOARD_PERMISSION_DENIED.

isClipboardReadSupported()

Check if clipboard read operations are available and usable in the current context.

function isClipboardReadSupported(): boolean

Returns: 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-read query. This function uses synchronous feature detection only.

copyToClipboardLegacy(text, options?)

Copy text to the clipboard using the deprecated document.execCommand('copy') API.

function copyToClipboardLegacy(text: string, options?: ClipboardOptions): boolean

Returns: 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.
  • execCommand copy is not reliably supported on iOS Safari. This function may return false on iOS Safari without a usable fallback.

Error Handling

All clipboard functions accept an optional onError callback that receives a structured BrowserUtilsError object. Functions never throw -- they return false or null on failure.

BrowserUtilsError

interface BrowserUtilsError {
  code: ErrorCode
  message: string
  cause?: unknown
}

type ErrorCode =
  | 'CLIPBOARD_NOT_SUPPORTED'
  | 'CLIPBOARD_PERMISSION_DENIED'
  | 'CLIPBOARD_WRITE_FAILED'
  | 'CLIPBOARD_READ_FAILED'
  | 'INSECURE_CONTEXT'

ClipboardOptions

interface ClipboardOptions {
  onError?: (error: BrowserUtilsError) => void
}

Error Codes

| 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 |

Example: Using onError

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.

Browser Support

See the browser support table in the root README. All functions require ES2020+ (>95% global browser support) and are SSR-safe.

License

MIT