golang.design/x/clipboard: one clipboard API for Go across every desktop and mobile platform
📋 cross-platform clipboard package that supports accessing text and image in Go (macOS/Linux/Windows/Android/iOS)
At a glance
- What is it?
- The clipboard package gives Go programs the same text, image, file and custom MIME type clipboard access on macOS, Windows, Linux, the BSDs, iOS, Android and in the browser, and it does so without cgo on the desktop, so there is no C compiler at build time and no X11 or Wayland library to ship. The Linux backend picks Wayland or X11 by itself depending on what the compositor offers.
- Who is it for?
- Adopt golang.design/x/clipboard when you are writing a Go tool that reads or writes the clipboard and you want the same code to work on every platform without cgo. Do not expect identical behaviour everywhere, because the feature table is explicitly uneven, with images and files desktop-only and the browser limited to text.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 27 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The one API and the no-cgo claim
The package's pitch is one line: copy and paste from Go, the same way on every platform. The import path is golang.design/x/clipboard, and the repository lives under the golang-design organisation, which is the Golang design initiative rather than a personal account.
What the feature list says is that it handles text, images, files and any MIME type you register. The platform list is macOS, Windows, Linux, FreeBSD, OpenBSD, NetBSD, iOS, Android and the browser.
The third bullet is the one that matters most for anyone embedding this in a distributed binary. There is no cgo on the desktop: no C compiler to build and no libX11 or libwayland to run. That is a significant departure from the usual approach to a Linux clipboard in Go, which is to link against X11 through cgo and ship the shared libraries with your binary or require them on the target machine.
The go.mod shows how that is achieved. The desktop path depends on github.com/ebitengine/purego, which is a pure Go implementation of the foreign function interface, and on golang.design/x/x11, which is a Go port of the X11 protocol rather than a binding to the C library. The result is that the X11 client speaks the wire protocol directly instead of calling into libX11.
The module requires Go 1.24, and the other direct dependencies are golang.org/x/image for image codecs and golang.org/x/mobile for the gomobile bridge used by the iOS and Android backends. There is also an indirect dependency on golang.org/x/exp/shiny, which is the Go windowing layer the mobile backends sit on.
How Linux decides between Wayland and X11
The Linux backend is the interesting engineering in the package, because Wayland has no clipboard protocol of its own and every project has solved that differently.
The README says the package picks Wayland by itself when the compositor offers the data-control protocol, naming GNOME 49 and later, KDE Plasma, Sway, Hyprland and other wlroots compositors, and picks X11 otherwise, which under an older Wayland compositor means XWayland. Nothing needs configuring.
That is a large amount of compatibility work hidden behind one sentence. The data-control protocol is the compositor-side clipboard extension, and a program that speaks it directly does not need the XWayland bridge at all, which is what makes a native Wayland path possible rather than merely emulated.
The source tree shows the split. There is a clipboard_wayland.go and a clipboard_x11.go alongside clipboard_linux.go, and the test files are organised the same way, with separate custom, enumerate, files, loops, primary, sensitive and writeall tests for Linux and for Wayland, plus an internal test for unowned X11 windows, which is the case where a window has no owner and the clipboard ownership rules behave differently.
The platform table also records what each backend needs at runtime. macOS talks to the system pasteboard and needs nothing. Windows talks to the Win32 clipboard and needs nothing. Linux and FreeBSD need a Wayland compositor with data-control, or an X server. OpenBSD and NetBSD need an X server, with Wayland marked untested. iOS and Android use the system clipboard through gomobile and need nothing, but text only. The browser uses navigator.clipboard and needs https, also text only.
The CI story backs the claim of portability. The full test suite runs on macOS, Windows, Linux with both X11 and Wayland, and FreeBSD with Wayland, with builds for OpenBSD and NetBSD described as best-effort.
The feature table, and what happens where a feature is missing
The README's second table is the most useful thing in the documentation, because it tells you exactly which feature works where, and it defines the term desktop as macOS, Windows, Linux and the BSDs.
Text through FmtText works everywhere. Images through FmtImage are desktop only. Files through ReadFiles, WriteFiles and FmtFiles are desktop only. Your own MIME types through Register are desktop only. Several formats in one copy through WriteAll are desktop only. Listing what is on the clipboard through Formats is desktop only. Watching for changes through Watch works everywhere except the browser. The middle-click clipboard through FromPrimary is X11 and Wayland only. Forgetting after N pastes through Loops is X11 and Wayland only. Spotting passwords from a password manager through Sensitive and Data.Sensitive is desktop only.
Then the behaviour when a feature is missing is specified, which is the part that makes the table usable. Where a feature is missing, the call returns ErrUnsupported or does the nearest safe thing: WriteAll publishes only your first item, Formats returns an empty list, and Loops is ignored.
That is a good design choice for a cross-platform package, because a caller gets either a clear error or a defined degradation rather than a panic or a silent wrong answer. It also means your code has to check the error, which is where a lot of the value is.
Two of those features deserve a note because they are unusual. FromPrimary exposes the X11 and Wayland selection clipboard, the one you get with the middle mouse button, which is a separate buffer from the main clipboard and is used by some terminals for pasting. Loops implements the clipboard behaviour where a paste count decrements and the entry disappears after a set number of pastes, which is how a password manager on Linux makes a secret vanish from the clipboard after use. The Sensitive flag marks a payload as coming from a password manager so that the clipboard will not advertise it.
The API surface: reading, writing, watching and custom formats
The usage section is built around a context-aware API, and the context is not decoration.
Every call takes a context.Context, which is checked before the call starts, and on X11 a read also gives up at your deadline. That second point is a real answer to a real problem, because an X11 read can block on the selection owner, and a clipboard read that hangs forever is a bug you cannot diagnose.
The basic calls are short:
clipboard.Write(ctx, clipboard.FmtText, []byte("some text")) // UTF-8
clipboard.Write(ctx, clipboard.FmtImage, pngBytes) // PNG
text, err := clipboard.Read(ctx, clipboard.FmtText)
img, err := clipboard.Read(ctx, clipboard.FmtImage) // always PNGImages always come out as PNG, because PNG keeps transparency and every platform understands it. Write accepts other encodings once you import the decoder, converting them to PNG for you, which is why the import list includes a blank image/jpeg or the webp package. If you need the bytes on the clipboard unchanged, a JPEG that stays a JPEG, you use a custom format instead.
The error handling is specified by sentinel value, which is the pattern to follow in your own code:
b, err := clipboard.Read(ctx, clipboard.FmtText)
switch {
case errors.Is(err, clipboard.ErrNoData): // nothing on the clipboard in this format
case errors.Is(err, clipboard.ErrUnavailable): // no clipboard to reach
case errors.Is(err, clipboard.ErrUnsupported): // this platform cannot do that
}Two more capabilities go beyond reading and writing. Write returns a channel that fires once, when something else replaces what you wrote, so you can know your copy is gone. The data is on the clipboard as soon as Write returns, and the channel never fires if nobody else writes, so you should not wait on it to learn that the write finished. And Watch gives you a channel of changes:
for data := range clipboard.Watch(ctx, clipboard.FmtText) {
fmt.Println(string(data.Bytes))
}Pass several formats, or none to watch text, images and files together, and each value says which format changed. The channel closes when the context is canceled. Wayland and Windows report changes as they happen, and the other platforms check once a second, which is a latency difference worth knowing before you build on it.
Custom MIME types, rich text and file transfers
The most flexible part of the API is the custom format registration, and it is where the package goes beyond plain text.
Any MIME type can be used as a format, whether that is text/html, image/jpeg or application/pdf:
html := clipboard.Register("text/html")
clipboard.Write(ctx, html, []byte("<b>hi</b>"))
b, err := clipboard.Read(ctx, html)The bytes are moved exactly as given, with no conversion, and Register returns the same value every time for the same MIME type, and can be called before Init. ReadAs does the read and the decode in one step.
There is a translation step on macOS and Windows. Common MIME types are translated to the names other applications use there, so image/png becomes public.png on macOS and PNG on Windows, which is what makes browsers, Office and Preview understand what you put on the clipboard. Any other type is used as it is: it round-trips through the package but other applications may not recognise it.
That distinction is the important one for anyone writing a rich-text or file-transfer feature. The package can move any bytes you like, but interoperability with other applications depends on the format being one the platform has a name for.
For several formats in one copy, WriteAll takes items most preferred first:
html := clipboard.Register("text/html")
clipboard.WriteAll(ctx,
clipboard.Item{Format: html, Bytes: []byte("<b>hi</b>")},
clipboard.Item{Format: clipboard.FmtText, Bytes: []byte("hi")},
)This is the copy as rich text pattern, where the destination application takes the best format it understands. The README is explicit that calling Write twice does not do this, because every write replaces the whole clipboard, so only the last format would be left.
Files are copied the way a file manager does, so they paste into Finder, Explorer, Nautilus and the equivalents.
Limits: uneven features, a real dependency on a display, and a shared-maintainer load
The limits are visible in the tables, and they are real.
The first is that the feature set is uneven by design, and the degradation is sometimes silent in a way you have to code around. WriteAll publishes only your first item on a platform that does not support it, and Loops is ignored entirely. Neither returns an error. A program that assumes multi-format copy works everywhere will quietly publish the wrong thing on mobile and in the browser, and only on a desktop-only assumption would you notice.
The second is that the package needs a display to talk to. Init fails if there is no clipboard to talk to, such as on a Linux server with no display, and the error is fatal in the quick start example. That is correct behaviour but it means the package cannot be used on a headless server without a virtual display, and the Dockerfile in the repository shows what that takes: it installs xvfb alongside the X11 and EGL development libraries and runs the test script under it.
The third is the Wayland dependency. The native Wayland path needs a compositor offering the data-control protocol, and while the fallback to XWayland is automatic, the behaviour under an older Wayland compositor is the X11 behaviour rather than the native one, which means the middle-click selection and the Loops support come from the X11 path.
The fourth is the change-detection latency. Wayland and Windows report changes as they happen, and everything else checks once a second, so a watch loop on macOS or the BSDs has up to a second of delay. That is fine for a clipboard manager and wrong for something that needs to react instantly to a copy.
The fifth is the project scope. This is one package maintained by an initiative rather than a team, and the recent release history shows the pace: v0.9.0 on 2026-08-21, v0.10.0 on 2026-09-25 and v0.11.0 on 2026-09-26. That is three releases in a month, which is good news for responsiveness and means the API can still move.
This package against calling the platform APIs directly and against shelling out
Two comparisons make the choice clearer.
The first is against writing the platform calls yourself. On macOS that is the NSPasteboard API through cgo or a bridge. On Windows it is the Win32 clipboard functions, and the repository has a dib.go file with a test file beside it, which is the Device Independent Bitmap handling that the Win32 clipboard requires. On Linux it is X11, either through cgo bindings or by speaking the protocol. The difference in approach is that this package has already done that work, once, for nine platforms, and offers it behind one API with one error vocabulary.
What writing it yourself buys is control and understanding. You would know exactly what happens on your platform, you would not carry the abstraction's compromises, and you would have one platform's API to maintain instead of a dependency on someone else's. What it costs is that the X11 and Wayland work alone is a project, and the Darwin and Windows work is another.
The second comparison is against shelling out to a command line tool such as pbcopy or xclip. That is the traditional approach in scripts, and it is a reasonable one for a one-off. The differences are that a subprocess is another thing to manage, that its behaviour depends on which tools happen to be installed, and that it cannot do the things this package does well: custom MIME types, several formats in one copy, watch, the primary selection, or the sensitive flag for password manager payloads.
The reason to pick this package is the last point in the table rather than the first. It is the only one of the three approaches that covers the awkward clipboard features without you implementing them per platform. If your program copies text and nothing else, and you have no objection to a subprocess, then the direct route is fine.
Editorial conclusion
Adopt golang.design/x/clipboard when you are writing a Go tool that reads or writes the clipboard and you want the same code to work on every platform without cgo. Do not expect identical behaviour everywhere, because the feature table is explicitly uneven, with images and files desktop-only and the browser limited to text. Verify first that the formats your application needs are supported on the platforms you ship to, that a Wayland compositor with data-control is present if you rely on the native Wayland path, and that the change-detection latency on your target platform fits your use, since most platforms poll once a second.
Frequently asked questions
Which platforms does golang.design/x/clipboard support and what does each need?
The README lists macOS talking to the system pasteboard, Windows talking to the Win32 clipboard, Linux and FreeBSD talking to Wayland or X11, OpenBSD and NetBSD on X11 with Wayland untested, iOS and Android via gomobile with text only, and the browser through navigator.clipboard with https and text only. CI runs the full suite on macOS, Windows, Linux with X11 and Wayland, and FreeBSD with Wayland.
Does the clipboard package need cgo on Linux?
No. The README states there is no cgo on the desktop, so there is no C compiler to build and no libX11 or libwayland to run. The go.mod depends on github.com/ebitengine/purego and on golang.design/x/x11, a Go port of the X11 protocol, and on Linux the package selects Wayland by itself when the compositor offers the data-control protocol and X11 otherwise.
What happens when a clipboard feature is not supported on a platform?
The call returns ErrUnsupported or does the nearest safe thing. The README specifies the degradations: WriteAll publishes only your first item, Formats returns an empty list, and Loops is ignored. The error values you can check for are ErrNoData for nothing in that format, ErrUnavailable for no clipboard to reach, and ErrUnsupported for a platform that cannot do it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/golang-design-clipboard)Was this useful?