DenchClaw Clipboard API: Copy Data Between Apps
How DenchClaw's clipboard API lets custom apps read and write clipboard data for seamless data transfer between your tools.
DenchClaw Clipboard API: Copy Data Between Apps
DenchClaw's clipboard API gives custom Dench Apps programmatic access to the system clipboard — for both reading and writing. This enables seamless data transfer between DenchClaw apps and external tools: copy a table of CRM data to paste into a spreadsheet, paste in a list of contacts for bulk import, or sync clipboard content between custom apps.
The Clipboard API Overview#
The dench.clipboard namespace is part of the DenchClaw bridge API available in all Dench Apps. It provides:
// Write text to clipboard
await dench.clipboard.write("Hello, clipboard!");
// Write structured data
await dench.clipboard.write(JSON.stringify(data), {
mimeType: "application/json"
});
// Read current clipboard content
const text = await dench.clipboard.read();
// Read clipboard as a specific type
const data = await dench.clipboard.read({
mimeType: "text/plain"
});
// Check clipboard contents without reading
const types = await dench.clipboard.getAvailableTypes();
// Returns: ["text/plain", "text/html"]
// Listen for clipboard changes
dench.clipboard.onChange((newContent) => {
console.log("Clipboard changed:", newContent);
});Permissions#
Clipboard access requires explicit permission in your app's .dench.yaml:
name: Contact Exporter
permissions:
db: read
clipboard: write # write only (most common)
# clipboard: read # read-only
# clipboard: readwrite # full accessDenchClaw separates read and write permissions because reading the clipboard is more sensitive than writing to it. Read permission should only be requested if your app genuinely needs to inspect the clipboard.
Common Use Case: Copy CRM Data as CSV#
The most common clipboard use case: let users copy CRM data to paste into Excel, Google Sheets, or another tool.
// Query CRM data
const deals = await dench.db.query(`
SELECT "Deal Name", "Company", "Stage", "Deal Value", "Close Date"
FROM v_deals
WHERE "Status" = 'Active'
ORDER BY "Deal Value" DESC
`);
// Convert to CSV
function toCSV(rows) {
if (!rows.length) return '';
const headers = Object.keys(rows[0]).join(',');
const dataRows = rows.map(row =>
Object.values(row).map(v =>
typeof v === 'string' && v.includes(',') ? `"${v}"` : v
).join(',')
);
return [headers, ...dataRows].join('\n');
}
const csv = toCSV(deals);
// Write to clipboard
await dench.clipboard.write(csv);
// Show success feedback
await dench.ui.toast({ message: "Pipeline data copied to clipboard!", type: "success" });Now the user can press Cmd+V in Excel and get a fully formatted spreadsheet.
Copy as Formatted HTML Table#
For pasting into email clients, Notion, or rich text editors:
const contacts = await dench.db.query(`
SELECT "Full Name", "Email", "Company", "Status"
FROM v_people
WHERE "Status" = 'Active'
LIMIT 20
`);
// Build HTML table
const html = `
<table>
<thead>
<tr>${Object.keys(contacts[0]).map(k => `<th>${k}</th>`).join('')}</tr>
</thead>
<tbody>
${contacts.map(row =>
`<tr>${Object.values(row).map(v => `<td>${v || ''}</td>`).join('')}</tr>`
).join('')}
</tbody>
</table>
`;
// Write both HTML and plain text versions
await dench.clipboard.writeMultiple([
{ mimeType: "text/html", content: html },
{ mimeType: "text/plain", content: contacts.map(c => `${c['Full Name']}\t${c['Email']}`).join('\n') }
]);Pasting into an email client gives a formatted table. Pasting into a plain text field gives tab-separated values.
Paste Into DenchClaw: Reading Clipboard for Import#
Allow users to paste data from external sources into your app for processing:
// User clicks "Paste leads" button
document.getElementById('paste-btn').onclick = async () => {
const text = await dench.clipboard.read();
if (!text) {
await dench.ui.toast({ message: "Nothing on clipboard", type: "warning" });
return;
}
// Parse as CSV
const rows = parseCSV(text);
// Show preview
document.getElementById('preview').innerHTML = `
<p>Found ${rows.length} rows. Fields: ${Object.keys(rows[0]).join(', ')}</p>
`;
// Map to CRM fields and import
document.getElementById('import-btn').onclick = async () => {
for (const row of rows) {
await dench.objects.createEntry('people', {
'Full Name': row.name || row['Full Name'],
'Email': row.email || row['Email'],
'Company': row.company || row['Company'],
'Status': 'Lead'
});
}
await dench.ui.toast({ message: `Imported ${rows.length} contacts`, type: "success" });
};
};Inter-App Clipboard Coordination#
Two DenchClaw apps can coordinate via clipboard. App A writes structured data; App B reads and processes it:
App A (Contact Selector) writes:
const selected = getSelectedContacts(); // user selections
await dench.clipboard.write(JSON.stringify({
type: "dench:contacts",
ids: selected.map(c => c.id)
}));App B (Email Composer) reads:
const raw = await dench.clipboard.read();
if (raw.startsWith('{"type":"dench:contacts"')) {
const { ids } = JSON.parse(raw);
const contacts = await dench.db.query(
`SELECT * FROM v_people WHERE id IN (${ids.map(id => `'${id}'`).join(',')})`
);
// Pre-populate email recipients
populateRecipients(contacts);
}This pattern enables lightweight coordination between apps without a shared message bus.
Best Practices#
- Always confirm before writing to clipboard — show what will be written and ask for confirmation, or provide clear UI feedback after writing
- Use
writeMultiplefor rich content — provide both HTML and plain text so paste behavior adapts to the target application - Check permissions — clipboard.read requires explicit permission; request it only if necessary
- Handle errors — clipboard access can fail (security policy, browser restrictions in non-PWA contexts); always wrap in try/catch
See also: DenchClaw Inter-App Messaging for a more structured approach to data passing between apps, and DenchClaw HTTP Proxy for getting data from external sources.
Frequently Asked Questions#
Does clipboard access require user permission?#
Clipboard write is available to Dench Apps with clipboard permission declared in their manifest, no additional user prompt. Clipboard read may require a browser permission prompt in non-PWA contexts. In the DenchClaw PWA (recommended), both read and write work without prompts.
Can I write images to the clipboard?#
Yes. dench.clipboard.write(imageBlob, { mimeType: "image/png" }) writes image data. This allows apps to copy charts, screenshots, or generated images that users can paste into design tools or presentations.
Does clipboard data leave my machine?#
Clipboard data is managed by your operating system. When your Dench App writes to the clipboard, the data lives in your OS clipboard — the same as if you manually Cmd+C'd it. DenchClaw itself doesn't transmit clipboard data anywhere.
What's the maximum size for clipboard content?#
OS-imposed limits vary but are typically in the hundreds of megabytes. Practically, keep clipboard data under 10MB for reliable pasting behavior. For large datasets, export to a file instead.
Can clipboard listeners work in a widget?#
Yes. Register a clipboard change listener in your widget and the widget will update its display when relevant clipboard content is detected — for example, a "Paste and Import" widget that activates when it detects CRM-formatted data on the clipboard.
Ready to try DenchClaw? Install in one command: npx denchclaw. Full setup guide →
