Writing Clipboard

How to programmatically copy text, HTML, and images to the system clipboard from a Tauri v2 app using the clipboard-manager plugin

The system clipboard is the shared pasteboard that lets you cut, copy, and paste between applications. When a user presses Ctrl+C, the highlighted content lands there. Tauri’s clipboard plugin gives your app the same ability—your React frontend or Rust backend can place data into the clipboard without any user gesture, as long as the right permissions are enabled.

This page covers writing data. For reading from the clipboard, see Reading Clipboard.

Plugin already set up?:

The clipboard-manager plugin must be installed and registered in your Tauri project. If you haven't done that, go back to the Clipboard API Introduction first.

What writing to the clipboard actually does

Calling a write function replaces the entire clipboard content with the new data. The previous content is gone. Other applications (text editors, browsers, terminals) can then paste that content as if the user had pressed Ctrl+V. This works across all native applications on Windows, macOS, and Linux. On Android and iOS, only plain text writing is available—the system restricts richer formats.

Under the hood, the Tauri plugin calls operating-system-level clipboard APIs. On Windows it uses the Win32 clipboard functions, on macOS it uses NSPasteboard, and on Linux it goes through GTK or the X11/Wayland clipboard. Your JavaScript code just calls a Promise-based function; Tauri’s Rust core handles the platform specifics.

For a beginner, you can think of the clipboard as a single slot that holds one item at a time. Writing to it is like placing a fresh piece of paper on the desk—any old paper falls off.

Writing plain text

Plain text is the most universal clipboard format. Every application that accepts pasted content understands it. Use writeText when you want to copy a string of text: a username, a URL, a code snippet, a message.

Permissions

The command requires the clipboard-manager:allow-write-text permission. If that permission is missing, the call will throw a runtime error. (You normally configure this in a capability file—the Introduction page covers that.)

From JavaScript

import { writeText } from "@tauri-apps/plugin-clipboard-manager";
function CopyTextButton() {
  const handleCopy = async () => {
    try {
      await writeText("Tauri is awesome!");
      console.log("Text copied to clipboard");
    } catch (error) {
      console.error("Failed to write text:", error);
    }
  };
  return <button onClick={handleCopy}>Copy greeting</button>;
}
export default CopyTextButton;

The JavaScript example uses an async handler because writeText returns a Promise. If the clipboard is busy or the permission is denied, the Promise rejects—catching that prevents a silent failure. The Rust code calls .unwrap() for brevity; in production you’d handle the Result properly.

Check for success:

If you open a text editor after clicking the button and paste, you should see “Tauri is awesome!” appear. The function succeeded.

Copying a URL

A URL is just plain text with a scheme. The same function works:

const copyUrl = async () => {
  await writeText("https://v2.tauri.app/");
};

Paste it into a browser’s address bar or a note, and it works because the clipboard holds exactly that string.

Overwriting the clipboard:

Every call to writeText replaces whatever was previously in the clipboard. If the user had something important copied, it’s gone. In user-facing features, consider showing a brief confirmation or only writing on explicit user action to avoid accidental data loss.

Writing HTML content

HTML writing allows you to place rich text into the clipboard. When the user pastes into a rich-text editor, an email client, or a word processor, the formatting (bold, links, colors) is preserved. The function is writeHtml.

Permissions

This requires clipboard-manager:allow-write-html. Without it, the write fails.

From JavaScript

import { writeHtml } from "@tauri-apps/plugin-clipboard-manager";
function CopyRichMessage() {
  const handleCopy = async () => {
    const html = "<p>Check out <a href='https://tauri.app'>Tauri</a> — it's <strong>fast</strong>!</p>";
    try {
      await writeHtml(html);
    } catch (error) {
      console.error("Failed to write HTML:", error);
    }
  };
  return <button onClick={handleCopy}>Copy rich message</button>;
}

From Rust

app.clipboard()
    .write_html("<b>Bold text</b>".to_string())
    .unwrap();

Paste the result into a rich-text editor and you’ll see a clickable link with bold text. Paste it into a plain-text-only field (like a terminal or a basic <textarea>), and the behavior depends on the operating system: some platforms strip tags and show the text content (“Check out Tauri — it’s fast!”), others may show the raw HTML markup.

No automatic plain-text fallback:

The official plugin’s writeHtml does not set a separate plain-text representation. On platforms that do not automatically extract text from HTML, pasting into a plain-text context may show nothing or raw markup. If your use case requires both rich and plain-text fallback, consider writing text with writeText first, then calling writeHtml. However, the second write will overwrite the clipboard, so the order must be tested on your target platforms. The upcoming Reading Clipboard page shows how to verify what’s actually stored.

Writing images

You can copy an image to the clipboard so that it can be pasted directly into image editors, document tools, or chat applications. The plugin’s writeImage accepts a base64-encoded image string.

Permissions

Enable clipboard-manager:allow-write-image. Without it, the command throws an error.

From JavaScript

The frontend needs a base64 string. You might get this from a canvas, a file picker, or a preloaded asset. Here’s a minimal example with a hardcoded tiny PNG in base64:

import { writeImage } from "@tauri-apps/plugin-clipboard-manager";
function CopyImageButton() {
  // A 1x1 red pixel PNG encoded in base64
  const tinyImageBase64 =
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==";
  const handleCopy = async () => {
    try {
      await writeImage(tinyImageBase64);
      console.log("Image copied to clipboard");
    } catch (error) {
      console.error("Failed to write image:", error);
    }
  };
  return <button onClick={handleCopy}>Copy sample image</button>;
}

Large images may be slow:

Encoding a large image as base64 and sending it through the Tauri IPC bridge can be slow and memory-intensive. For high-resolution photos, consider using the Rust side to write the image bytes directly, which avoids the JavaScript encoding overhead.

From Rust

Rust can write an image from a file path or raw bytes. The exact API depends on the plugin version; consult the latest API reference. In typical usage:

use tauri_plugin_clipboard_manager::ClipboardExt;
// Writing base64 directly (hypothetical – check the actual method)
app.clipboard()
    .write_image_base64(base64_string)
    .unwrap();

Mobile platforms only support plain text:

The official plugin documentation states that Android and iOS currently support only plain-text clipboard content. Writing HTML or images will have no effect on those platforms. Always check the platform before exposing rich-media clipboard features.

Common mistakes

Even with a straightforward API, a few patterns trip people up. Here are the ones worth knowing.

Forgetting to await the Promise

writeText returns a Promise. If you call it without await and don’t attach .catch(), you’ll never see an error and the write may silently fail. Always use await inside an async function or handle the promise chain.

Missing permissions

If you haven’t added the correct allow-write-* permission in a capability file, the command will reject. The error message in the console will mention a denied permission. Double-check the permission identifier matches exactly—clipboard-manager:allow-write-text, not a typo.

Assuming writeHtml sets plain text

As noted earlier, writing HTML does not guarantee a plain-text representation on every OS. If your users report that pasting into a plain-text field shows nothing, this is the likely cause. Test on Windows, macOS, and Linux separately.

Writing from the wrong thread in Rust

If you call clipboard functions outside the main thread without using Tauri’s app handle correctly, you may hit runtime panics. Always use the AppHandle or App instance provided by Tauri’s setup or command system.