![Thomas Steiner](https://web.dev/images/authors/thomassteiner.jpg) Thomas Steiner [GitHub](https://github.com/tomayac) [LinkedIn](https://www.linkedin.com/in/thomassteinerlinkedin) [Mastodon](https://toot.cafe/@tomayac) [Bluesky](https://bsky.app/profile/tomayac.com) [Homepage](https://blog.tomayac.com/)

<br />

Until now, the [Async Clipboard API](https://web.dev/articles/async-clipboard) supported a limited set of
MIME types to be copied to and pasted from the system clipboard, specifically: `text/plain`,
`text/html`, and `image/png`. The browser typically sanitizes this to, for example, remove embedded
`script` elements or `javascript:` links from an HTML string, or to prevent PNG
[decompression bomb](https://en.wikipedia.org/wiki/Zip_bomb) attacks.

In some cases, though, it can be desirable to support unsanitized content on the clipboard:

- Situations where the application deals with the sanitization itself.
- Situations where it's crucial for the copied data to be identical with the pasted data.

For such cases, the Async Clipboard API now supports web custom formats that let developers write
arbitrary data to the clipboard.

## Browser support

The Async Clipboard API per se with image support is supported as of Chromium 76. Web custom
formats for the Async Clipboard API are supported on desktop and on mobile Chromium as of
version 104.

## Writing web custom formats to the clipboard

Writing web custom formats to the clipboard is almost identical to
[writing sanitized formats](https://developer.chrome.com/%3Chttps://web.dev/async-clipboard#write()%3E), except for the requirement
to prepend the string `"web "` (including the trailing space) to the blob's MIME type.

    // Fetch remote JPEG and GIF images and obtain their blob representations.
    const [jpegBlob, gifBlob] = await Promise.all([
      fetch('image.jpg').then((response) => response.blob()),
      fetch('image.gif').then((response) => response.blob()),
    ]);

    try {
      // Write the image data to the clipboard, prepending the blobs' actual
      // types (`"image/jpeg"` and "image/gif") with the string `"web "`, so
      // they become `"web image/jpeg"` and `"web image/gif"` respectively.
      // The code elegantly makes use of computed property names:
      // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names.
      const clipboardItem = new ClipboardItem({
        [`web ${jpegBlob.type}`]: jpegBlob,
        [`web ${gifBlob.type}`]: gifBlob,
      });
      await navigator.clipboard.write([clipboardItem]);
    } catch (err) {
      console.error(err.name, err.message);
    }

## Reading web custom formats from the clipboard

As with writing, reading web custom formats from the clipboard is almost identical to
[reading sanitized formats](https://developer.chrome.com/%3Chttps://web.dev/async-clipboard#read()%3E). The only difference is that
the app now needs to look for clipboard items whose type starts with `"web "`.

    try {
      // Iterate over all clipboard items.
      const clipboardItems = await navigator.clipboard.read();
      for (const clipboardItem of clipboardItems) {
        for (const type of clipboardItem.types) {
          // Discard any types that are not web custom formats.
          if (!type.startsWith('web ')) {
            continue;
          }
          const blob = await clipboardItem.getType(type);
          // Sanitize the blob if you need to, then process it in your app.
        }
      }
    } catch (err) {
      console.error(err.name, err.message);
    }

## Interoperability with platform-specific apps

Web custom formats like `web image/jpeg` are not something that typical platform-specific
applications understand (since they would expect `image/jpeg`). Over time, concerned apps are
expected to add support for such formats as an opt-in if their developers deem support for web
custom formats to be relevant for their users. On the operating system clipboard, the various
formats are present in multiple formats ready for consumption, as can be seen in the
screenshot for macOS below.

![Clipboard expector on macOS showing a custom format map listing two web custom formats.](https://developer.chrome.com/static/blog/web-custom-formats-for-the-async-clipboard-api/image/clipboard-expector-macos-aad7352434aa1.png)

## Demo

You can try the demo and
[view the source code](https://glitch.com/edit/#!/custom-async-clipboard) to see how the demo works.
<iframe src="https://custom-async-clipboard.glitch.me/" title="custom-async-clipboard on Glitch" allow="clipboard-read; clipboard-write" style="height: 100%; width: 100%; border: 0;">
</iframe>

## Acknowledgements

This document was reviewed by [Joe Medley](https://github.com/jpmedley)
and [François Beaufort](https://github.com/beaufortfrancois).