Skip to content

@absoluteui/services/clipboard

Copy and read text, rich content, and images, with status for announcements.

import { ... } from '@absoluteui/services/clipboard';

Copies and reads text, rich content, and images, with a fallback for older or insecure contexts that keeps focus and selection where they were. Every function resolves to a status object instead of throwing, and describeCopyStatus turns it into text for a live region. Clipboard contents are never logged. The Files and downloads guide shows the basic copy; the recipes below cover the rest.

How do I build a “Copy” button that announces the result?

Section titled “How do I build a “Copy” button that announces the result?”

Copy from the click handler (browsers require a user gesture) and put the description in a polite live region. Pass labels to translate and shortcut to fix the key hint.

import { copyText, describeCopyStatus } from '@absoluteui/services/clipboard';
const region = document.createElement('div');
region.setAttribute('aria-live', 'polite');
const button = document.createElement('button');
button.textContent = 'Copy employee ID';
button.addEventListener('click', async () => {
region.textContent = describeCopyStatus(await copyText('EMP-000042'));
});
import { describeCopyStatus } from '@absoluteui/services/clipboard';
describeCopyStatus({ ok: true, method: 'clipboard-api' }, { labels: { copied: 'In die Zwischenablage kopiert' } }); // => 'In die Zwischenablage kopiert'
describeCopyStatus({ ok: false, method: 'clipboard-api', reason: 'denied' }, { shortcut: 'Ctrl+C' }); // => 'Clipboard access is blocked. Select the text and press Ctrl+C.'

How do I copy a table so it pastes into a spreadsheet?

Section titled “How do I copy a table so it pastes into a spreadsheet?”

copyRich puts several formats on the clipboard at once: spreadsheets and editors take the HTML, plain-text fields take the tab-separated text. Sanitize any HTML that contains user data first, for example with sanitizeHtml from @absoluteui/core.

import { escapeHtml } from '@absoluteui/helpers/string';
import { copyRich } from '@absoluteui/services/clipboard';
const rows = [['Name', 'Department'], ['Ada Lovelace', 'Finance'], ['Grace Hopper', 'Engineering']];
const html = `<table>${rows.map((r) => `<tr>${r.map((c) => `<td>${escapeHtml(c)}</td>`).join('')}</tr>`).join('')}</table>`;
const status = await copyRich({ 'text/plain': rows.map((r) => r.join('\t')).join('\n'), 'text/html': html });

How do I paste from the clipboard into a field?

Section titled “How do I paste from the clipboard into a field?”

readText checks the permission first, so a user who blocked access is not prompted again. Handle denied, empty, and unsupported with a hint to paste with the keyboard.

import { readText } from '@absoluteui/services/clipboard';
const input = document.createElement('input');
const hint = document.createElement('p');
const pasteButton = document.createElement('button');
pasteButton.addEventListener('click', async () => {
const r = await readText();
if (r.ok) input.value = r.text.trim();
else hint.textContent = r.reason === 'denied' ? 'Paste is blocked. Press Ctrl+V in the field instead.' : 'Nothing to paste.';
});

copyBlob(pngBlob) copies an image such as a chart; it has no fallback, and most browsers accept only PNG.

  • The async clipboard needs a secure context and a user gesture. Outside a secure context, text copies use the fallback (method: 'exec-command'); images and reads return reason: 'unsupported'.
  • Nothing to copy is reported, not thrown:
import { copyRich } from '@absoluteui/services/clipboard';
await copyRich({}); // => { ok: false, method: 'none', reason: 'empty' }

copyBlob

Copies a binary item such as an image/png Blob. There is no fallback; browsers generally accept only image/png, text/plain and text/html.

function copyBlob(blob: Blob): Promise<CopyStatus>;

Example:

const chart = document.createElement('canvas');
const png = await new Promise<Blob | null>((resolve) => chart.toBlob(resolve, 'image/png'));
if (png) await copyBlob(png);

copyRich

Copies rich content (for example HTML with a plain-text alternative) via ClipboardItem, falling back to a copy event that sets each type. HTML must already be sanitized.

function copyRich(data: RichClipboardData): Promise<CopyStatus>;

Example:

await copyRich({ 'text/plain': 'Total: 42', 'text/html': '<b>Total:</b> 42' }); // sanitize any user-supplied HTML first

copyText

Copies plain text. Uses the async Clipboard API in secure contexts, else (or when it is denied) an isolated execCommand('copy') fallback that keeps focus and selection. Call it from a user gesture.

function copyText(text: string): Promise<CopyStatus>;

Example:

const status = await copyText('https://hr.example.com/invite/7Gk2');
const liveRegion = document.createElement('div');
liveRegion.setAttribute('aria-live', 'polite');
liveRegion.textContent = describeCopyStatus(status);

describeCopyStatus

Text for a live region describing a copy status.

function describeCopyStatus(value: CopyStatus, options?: DescribeCopyStatusOptions): string;

Example:

describeCopyStatus({ ok: true, method: 'clipboard-api' }); // => 'Copied to clipboard'
describeCopyStatus({ ok: false, method: 'none', reason: 'unsupported' }, { shortcut: 'Ctrl+C' }); // => 'Copy failed. Select the text and press Ctrl+C.'
describeCopyStatus({ ok: false, method: 'none', reason: 'unsupported' }); // e.g. '… press Command+C.' on Apple platforms

readItems

Reads all clipboard items (text, HTML, images). Use item.types and item.getType(type).

function readItems(): Promise<ReadItemsResult>;

Example:

const r = await readItems();
const img = document.createElement('img');
if (r.ok) for (const item of r.items) if (item.types.includes('image/png')) img.src = URL.createObjectURL(await item.getType('image/png'));

readText

Reads plain text. Checks the permission first so a denied permission does not prompt again.

function readText(): Promise<ReadTextResult>;

Example:

const r = await readText();
const input = document.createElement('input');
if (r.ok) input.value = r.text;
else console.info(r.reason === 'denied' ? 'Paste permission is blocked.' : 'Nothing to paste.');

DEFAULT_COPY_LABELS

const DEFAULT_COPY_LABELS: CopyStatusLabels;

CopyStatus

Result of a copy.

interface CopyStatus {
readonly ok: boolean;
readonly method: ClipboardMethod;
readonly reason?: ClipboardFailureReason;
}

CopyStatusLabels

Live-region messages; override for localization.

interface CopyStatusLabels {
readonly copied: string;
readonly failed: string;
readonly denied: string;
readonly empty: string;
}

DescribeCopyStatusOptions

interface DescribeCopyStatusOptions {
readonly labels?: Partial<CopyStatusLabels>;
readonly shortcut?: string;
}

RichClipboardData

Rich clipboard payload keyed by MIME type.

interface RichClipboardData {
readonly 'text/plain'?: string;
readonly 'text/html'?: string;
}

ClipboardFailureReason

Why an operation failed.

type ClipboardFailureReason = 'denied' | 'unsupported' | 'empty' | 'error';

ClipboardMethod

How the operation was performed.

type ClipboardMethod = 'clipboard-api' | 'exec-command' | 'none';

ReadItemsResult

Result of readItems.

type ReadItemsResult = {
readonly ok: true;
readonly items: readonly ClipboardItem[];
} | {
readonly ok: false;
readonly reason: ClipboardFailureReason;
};

ReadTextResult

Result of readText.

type ReadTextResult = {
readonly ok: true;
readonly text: string;
} | {
readonly ok: false;
readonly reason: ClipboardFailureReason;
};