You can connect copy and paste on the page to an engine's clipboard or the OS clipboard by implementing a `Clipboard` handler. Clipboard data passes through the handler as whole, multi-format payloads.

## The Default Clipboard

`App::Create()` installs a default handler that connects to the OS clipboard.

`Renderer::Create()` installs no clipboard handler. A handler is optional— without one, copying on the page does nothing and pasting returns empty.

If you set a clipboard on `Platform` before calling `App::Create()` or `Renderer::Create()`, the renderer uses that handler instead (see [Setting Up the Platform](/docs/2.0/setting-up-the-platform)).

## Clipboard Payloads

Clipboard data is stored in `ClipboardData` as ordered entries.

Each entry holds text or raw bytes, keyed by a bare MIME type or an application-defined string.

The library reads and writes three standard types: `text/plain`, `text/html`, and `text/uri-list`.

Types must be bare MIME types without parameters (`text/plain`, rather than `text/plain;charset=utf-8`).

### Building a Payload

To build a payload with multiple formats, call `ClipboardData::Create()` and chain calls to `Set()`:

```cpp
#include <Ultralight/Ultralight.h>

using namespace ultralight;

void BuildPayload() {
  ///
  /// Richest format first, then the plain-text version.
  ///
  auto data = ClipboardData::Create();
  data->Set("text/html", "<b>Ultralight rocks!</b>")
       .Set("text/plain", "Ultralight rocks!");
}
```

Add the richest format first so the OS clipboard advertises formats in that order.

To create a payload with a single plain-text entry instead, pass the string directly to `ClipboardData::Create(text)`.

### Reading a Payload

Call `AsText()` or `AsHTML()` to read common text entries directly from a payload. Both methods return an empty string if that format is missing.

To inspect every entry, iterate through the payload using `size()`, `type_at()`, and `Get()` (as shown in the `Write()` example below).

## Implementing a Clipboard

To handle clipboard operations yourself, subclass `Clipboard` and pass an instance to `Platform::instance().set_clipboard()`.

The interface requires three methods: `Read()`, `Write()`, and `Clear()`. The library does not call `Clear()`, but you can call it to empty the OS clipboard.

The library calls these methods only on the Renderer's thread, so the implementation does not need to handle concurrent calls from multiple threads.

### Handling Pastes

The library calls `Read()` when a page pastes.

Return a payload containing one entry for each format you can read, providing at least `text/plain` when the clipboard holds text.

Returning `nullptr` is equivalent to returning an empty payload— both indicate an empty clipboard.

Whether JavaScript on the page can read the clipboard depends on `ViewConfig::clipboard_read_policy` (see [Text Editing and the Clipboard](/docs/2.0/text-editing-and-clipboard)).

### Handling Copies

The library calls `Write()` when a page copies, replacing the current contents of the clipboard. The passed payload is never null.

Write every supported format in a single OS clipboard transaction so a copy never partially replaces earlier data.

Skip any types you don't recognize— a backend that only supports plain text is fine.

Call `source_url()` on the payload to get the URL of the document where the copy originated (this may be empty).

### Example Implementation

The following implementation reads plain text from the OS clipboard and writes both plain text and HTML using pseudo-code platform calls:

```cpp
#include <Ultralight/Ultralight.h>

using namespace ultralight;

class MyClipboard : public Clipboard {
 public:
  void Clear() override {
    // Pseudo-code, empty the OS clipboard.
    OSClipboardClear();
  }

  RefPtr<ClipboardData> Read() override {
    ///
    /// We only read text. A null pointer is the same as an empty payload.
    ///
    // Pseudo-code, fetch the OS clipboard's text (empty if there is none).
    String text = OSClipboardGetText();
    if (text.empty())
      return nullptr;

    return ClipboardData::Create(text);
  }

  void Write(RefPtr<ClipboardData> data) override {
    ///
    /// One atomic transaction for the whole payload-- open once, write
    /// every entry we support, close once. Unknown types are skipped.
    ///
    // Pseudo-code, open and empty the OS clipboard.
    OSClipboardOpen();

    for (size_t i = 0; i < data->size(); ++i) {
      String type = data->type_at(i);
      ClipboardValue value = data->Get(type);
      if (type == "text/plain") {
        // Pseudo-code, write the OS text format.
        OSClipboardSetText(value.text());
      } else if (type == "text/html") {
        // Pseudo-code, write the OS HTML format (with the page it came from).
        OSClipboardSetHTML(value.text(), data->source_url());
      }
    }

    // Pseudo-code, close the OS clipboard.
    OSClipboardClose();
  }
};

MyClipboard clipboard;

void InitPlatform() {
  ///
  /// Install our clipboard before creating the Renderer. (Ownership
  /// stays with us, so 'clipboard' must outlive the Renderer.)
  ///
  Platform::instance().set_clipboard(&clipboard);
}
```
