I recently did some work on a VS Code extension whose purpose is to handle rich pastes. It prompted me to survey different copy-pasting libraries and the state of the NPM ecosystem as a whole.

How do clipboards work?

Clipboards across different operating systems work essentially the same. For this reason, we will stick to looking at a single one – Windows.

A common misconception about how clipboards work is that they contain a single piece of data, such as text or an image. In reality, clipboards hold all the ways target software can represent the data.

Consider if I copy the following webpage:

Headings HTML headings as rendered by Firefox

When pasting it into Microsoft Word, it appears formatted as rich content:

Microsoft Word Microsoft Word displays rich content exactly as Firefox does

When pasting it into Notepad++, it appears as plain text:

Notepad++ Notepad++ does not handle rich content

Pasting different content types depending on the software is possible because Firefox provides multiple representations of the rendered content to the clipboard.

Let’s see what types the clipboard is holding using Powershell:

$dataObj = [System.Windows.Forms.Clipboard]::GetDataObject()
foreach ($fmt in $dataObj.GetFormats()) {
    [Console]::WriteLine($fmt)
}

## Output: ##
HTML Format
System.String
UnicodeText
Text
Chromium Web Custom MIME Data Format
Locale
OEMText

We can look inside some of the clipboard types using Powershell, mainly HTML Format and Text with the following commands:

# Get Text
[System.Windows.Forms.Clipboard]::GetText([System.Windows.Forms.TextDataFormat]::Text)

## Output: ##
This is heading 1
This is heading 2
This is heading 3
This is heading 4
This is heading 5
This is heading 6

# Get HTML Format
[System.Windows.Forms.Clipboard]::GetText([System.Windows.Forms.TextDataFormat]::Text)

## Output: ##
Version:0.9
StartHTML:00000174
EndHTML:00000410
StartFragment:00000208
EndFragment:00000374
SourceURL:https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_headers
<html><body>
<!--StartFragment--><h1>This is heading 1</h1>
<h2>This is heading 2</h2>
<h3>This is heading 3</h3>
<h4>This is heading 4</h4>
<h5>This is heading 5</h5>
<h6>This is heading 6</h6><!--EndFragment-->
</body>
</html>

The way to approach clipboards as a consumer is to consume the clipboard formats you know how to, prioritizing some over others. For example, Microsoft Word will prioritize pasting HTML Format formats over Text formats by default.

While Powershell has some built-in parsers, implementing the format specification for every type of content is a lot of work. Are there libraries that can help us with this?

Copy-Pasting in the NPM Ecosystem

The simplest clipboard library would let us query the clipboard directly for binary data. This approach would require us to a parser per format per operating system. While such libraries deserve a place in this world, they are not helpful for most applications. Most applications want to handle copying and pasting only for text, rich text, images, and sometimes files. Let’s go through the different clipboard management libraries and see how they fare.

We are looking for a library that has the following features:

  • Pure JS, with native bindings
  • Cross-Platform Support
    • Windows
    • Linux
    • MacOS
  • Copy to clipboard
    • Plain Text
    • Rich Text
    • Images
    • Files
  • Paste from clipboard
    • Plain Text
    • Rich Text
    • Images
    • Files

Here is a table I made from the collected NPM packages, featuring the most popular clipboard-related libraries on NPM:

Note: This table does not include browser-based copy-paste.

Features / Libraries clipboardy copy-paste clipboard-cli node-clipboard-wd Electron (Not a library)
Pure JS Uses system utilties Uses system utilties Uses system utilties Chromium binary Uses native bindings
Cross-Platform Support Yes Yes Yes Yes Yes
Windows Yes Yes Yes Yes Yes
Linux Yes Yes Yes Yes Yes
MacOS Yes Yes Yes Yes Yes
Copy to Clipboard Partial Partial Partial No Yes
Plain Text Yes Yes Yes No Yes
Rich Text No No No No Yes
Images No No No No Yes
Files No No No No Exposes binary API
Paste from Clipboard Partial Partial Partial Yes Yes
Plain Text Yes Yes Yes Yes Yes
Rich Text No No No Yes Yes
Images No No No Yes Yes
Files No No No Yes Exposes binary API

Honorable mention: save-clipboard-image, uses AppleScript to save an image from clipboard to a file.

Summary

Before writing this post, I was unaware that the result would be this. It appears that all of NPM’s clipboard libraries work the same way: they call built-in executables on their host operating system and return the data. Interestingly, none of them handle images, rich text, or files despite no reason they couldn’t deal with them the same way.

The only exceptions to this pattern are Electron and node-clipboard-wd. The former is a framework unsuitable for usage as a library. The latter was written by myself last week as an experiment.

Where do we go from here?

Software developers have crossed the clipboard bridge in other environments before. In fact, NodeJS developers have already crossed it in Electron, which uses native bindings under the hood. There is no reason we couldn’t do the same using a more lightweight C++ library.

I find clip particularly promising in this regard, and I’ve been looking into writing a NodeJS wrapper around it. For posterity, here is a table of possible native libraries that I have found so far:

Features / Libraries clip arboard clipboard
Cross-Platform Support Yes Yes Yes
Windows Yes Yes Yes
Linux Yes Yes Yes
MacOS Yes Yes Yes
Copy to Clipboard Yes Partial Partial
Plain Text Yes Yes Yes
Rich Text Yes No No
Images Yes Yes Yes
Files Exposes binary API No No
Paste from Clipboard Yes Partial Partial
Plain Text Yes Yes Yes
Rich Text Yes No No
Images Yes Yes Yes
Files Exposes binary API No No