# Copy as Markdown, a browser extension for turning pages and tabs into Markdown

> yorkxin/copy-as-markdown is a cross-browser extension that copies a selection, a link, an image or a whole window of tabs to the clipboard as Markdown, in a GitHub-flavoured task list form. It ships on Chrome, Firefox and Edge, defaults to no keyboard shortcuts, and carries a build-time check that the Turndown HTML-to-Markdown library is not present.

**yorkxin/copy-as-markdown** — A browser extension to copy tabs and links as Markdown

- Repository: https://github.com/yorkxin/copy-as-markdown
- Stars: 762 · Forks: 102
- Language: TypeScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/yorkxin-copy-as-markdown

## What it copies and in what form

Copy as Markdown is a browser extension that copies things to the system clipboard as Markdown, and the README opens with the problem it solves: typing Markdown code manually for a link or an image, or for all the tabs in a window, is tiring.

From a web page it handles three things. Selection text as Markdown, so you highlight a paragraph and get it back as Markdown rather than as stripped plain text. A link on the page, which gives you the link syntax rather than the bare URL. And an image on the page, with or without the wrapping link, which is the case most extensions do not handle at all.

From the browser tabs it does four things. The current tab as a link. A list of links for all the tabs in the current window, or for the highlighted tabs. A task list in GitHub-flavoured Markdown, which is the checkbox syntax. And with tab grouping, in the browsers that support it, which is Chrome and Edge among the three this ships to.

The tab grouping option is the one that shows the author's attention to detail. Grouping tabs in the output means the Markdown mirrors the structure you had, rather than flattening everything into one long list.

The task list form is the other detail that makes this more than a link formatter. A list of links is easy; a list of links that is also a checklist, which you can tick off as you work through the items, is what you actually want when you have collected a set of references for an issue.

The extension is published on all three stores: the Chrome Web Store, the Firefox add-ons site and the Microsoft Edge Add-ons listing. The licence is MIT, with the terms in an MIT-LICENSE.txt file in the repository.

## Keyboard shortcuts and the one known issue

By default, Copy as Markdown assigns no keyboard shortcuts at all. The README is explicit about that and gives per-browser instructions for adding them.

For Firefox it points at the Firefox help page on managing extension shortcuts, which is the browser's own settings UI. For Chrome it names the URL chrome://extensions/shortcuts, with a note to paste the link into the location bar because the browser will not let you navigate to it from a link.

That default is worth a comment, because most extensions of this kind bind a shortcut. The reasoning is presumably that a copy shortcut that fires on the wrong combination is disruptive, and that users who want one will bind it deliberately. It also means the extension is invisible until you have opened its menu, which is a fair trade for an extension that writes to your clipboard.

The known issues section has one entry, and it is a real limitation rather than a cosmetic one. On Chrome, when copying an image, the image code does not include the alternative text of that image, and the README attributes this to API restrictions. So the output is a valid Markdown image, but it is missing the alt text, which matters for accessibility and for anything that renders the Markdown as HTML later.

It is worth knowing whether the same applies on Firefox, since the README frames the limitation as a Chrome one. The extension is the same code, so the difference must come from the clipboard or extension API each browser exposes, and that also means a behaviour difference between browsers is possible in other areas the README does not mention.

The practical takeaway is to test the image case on whichever browser you use before you build a habit around it, since alt text is the part a reader would notice missing.

## The build setup and the checks it runs on itself

The package.json shows a project with an unusual amount of self-checking, and those checks are the most telling thing about how it is maintained.

The Chrome build is not just a build. The build-chrome script runs the build, then runs an assertion that the Turndown library is not present, then runs a second assertion that no clipboard mock is present:

```bash
npm run build-chrome
```

The first assertion is interesting. Turndown is the standard JavaScript library for converting HTML into Markdown, and it is the obvious way to implement the selection-to-Markdown feature. Asserting that it is absent means the project has deliberately written its own conversion rather than pulling in a dependency that would handle the general case at the cost of a large bundle and imprecise output. For an extension whose whole job is producing Markdown, that is a reasonable call: a hand-written converter for a fixed set of cases can be both smaller and more predictable than a general HTML converter.

The second assertion checks that no clipboard mock has leaked into the production build. The extension writes to the real system clipboard, and a mock left in by accident would be a bug where the copy silently does nothing for users while the tests still pass. Asserting against it at build time is the kind of check that catches exactly that.

The build targets are separate, with build-chrome and build-firefox-mv3 producing the two browser variants and the Firefox one skipping the Turndown assertion, since that library is not used on either. The package scripts then zip the chrome directory and use web-ext build for Firefox. There are also separate debug scripts for chrome, edge and firefox-mv3, so you can load an unpacked build of any of the three.

Node 20 or newer is required, the package is an ES module, and the version in package.json is 2.0.0, which does not match the published v3.8.0 release, so the version is bumped by a script rather than by hand.

## Where the extension sits against the alternatives

The nearest alternative is the clipboard manager, and the comparison is close enough to be worth making precisely.

A clipboard manager, of which there are many, usually handles the case of copying rich web content and pasting it as plain text or as formatted HTML. Some of the better ones detect a URL and produce a Markdown link automatically, and a few offer Markdown output as an option. Where they generally stop is the multi-tab case, because copying a window full of tabs is a specific request that a clipboard history tool has no reason to model.

The second alternative is a page-scraping tool such as a readability-mode extension combined with a Markdown converter, which gives you much better Markdown for a whole article, but is the wrong shape for the quick case of grabbing one link and pasting it into a comment.

The third is doing it by hand, which is what the README's opening sentence describes as tiring. For one link, typing the syntax is fast. For a window of fifteen tabs, it is not.

The honest note on tradeoffs is that this extension does one thing and the quality depends on how well it handles the cases it covers rather than on how much it covers. That is a different bet from a general clipboard tool. It is also a bet that pays off in a specific workflow, which the README hints at with the GitHub-flavoured task list option: you collect references while reading, then paste a checklist of them into an issue and work through it.

The other difference worth noting is that it is an extension with a very small permission surface, writing to the clipboard and reading the page, with no account and no network round trip. For anyone who has decided not to install general clipboard managers because of what they might read, an extension that only formats and copies is an easier thing to justify.

The remaining question is the one the README cannot answer for you, which is whether the Markdown it produces matches the conventions your tools expect. The task list form is the specific one to check, since GitHub-flavoured Markdown task lists use a particular checkbox syntax and different renderers accept slightly different spacing.

## Testing and the repository layout

The test setup is unusually broad for an extension of this size, and it comes in two flavours.

Unit tests run on vitest, with a watch mode, a UI mode and a coverage mode. End-to-end tests run on Playwright, and the scripts show several variants: test:e2e builds a test extension first with node scripts/build-test-extension.js and then runs playwright test, with headed, UI and debug variants. There is a Docker route for CI through bash docker/playwright-ci/docker-e2e.sh.

And then there is a second end-to-end stack on Selenium with pytest, which needs its own environment: requirements.txt lists selenium, pytest, pyperclip and PyGObject. The script runs xvfb-run with a virtual framebuffer, because a browser extension needs a real display and the extension pages are not addressable in a headless browser. The Docker equivalent is bash docker/selenium-ci/docker-e2e.sh.

Having both Playwright and Selenium is unusual, and the reason is probably coverage across browsers rather than preference. Playwright drives Chromium easily, while Firefox and Edge need different handling, and the Selenium path with a virtual framebuffer is the way to run the extension inside Firefox.

The repository layout supports all of this. There are chrome/ and firefox-mv3/ directories for the two browser variants, src/ for the extension source, build/ and dist/ for output, test/ for the unit tests, e2e_test/ for the Selenium suite, fixtures/ for test data, scripts/ for the build and version tooling, docs/ for the build documentation, and docker/ for the CI environments. There is a playwright.config.ts and a vitest.config.ts, plus a .node-version and .python-version file and a mise.toml, which suggests the toolchain versions are pinned through a single manager.

There is also a CLAUDE.md at the root and a DEVELOPMENT.md that the README calls the canonical guide to building, type-checking, linting, debugging and running both test suites, with build internals in docs/build.md. The README points at those rather than repeating them, which keeps the main file short.

## Conclusion

Adopt Copy as Markdown if you write documentation, issue trackers or notes by hand and keep retyping link syntax, especially if you use GitHub-flavoured task lists. Skip it if you already have a clipboard tool that formats links, since the overlap is considerable. Verify first by installing it and testing the task list output with several tabs open, then confirm the image case behaves as you expect, since the README records a known limitation where the alternative text is not included on Chrome.

## FAQ

### What can Copy as Markdown copy?

From a web page it copies the selected text as Markdown, a link on the page, and an image on the page with or without a wrapping link. From the browser it copies the current tab as a link, a list of links for the window or the highlighted tabs, a GitHub-flavoured task list, and the same with tab grouping in Chrome and Edge.

### Does Copy as Markdown come with keyboard shortcuts?

No, by default it assigns none. The README explains how to add them, pointing at the Firefox extension shortcut settings and at the chrome://extensions/shortcuts page for Chrome.

### What is the known limitation of Copy as Markdown on Chrome?

When copying an image, the generated image code does not include the alternative text of that image, which the README attributes to API restrictions. The README lists this as the only known issue.

### Where is Copy as Markdown published?

On the Chrome Web Store, on the Firefox add-ons site and on the Microsoft Edge Add-ons listing, with a separate build target for the Chrome and Firefox Manifest V3 variants in the repository.

### How is Copy as Markdown tested?

Unit tests run on vitest and end-to-end tests run on Playwright, with a second end-to-end stack on Selenium and pytest that uses a virtual framebuffer through xvfb-run. The Selenium dependencies are listed in requirements.txt, and both suites have Docker scripts for CI.

## Sources

- [Issues](https://github.com/yorkxin/copy-as-markdown/issues)
- [License: MIT](https://github.com/yorkxin/copy-as-markdown/blob/master/LICENSE)
- [README](https://github.com/yorkxin/copy-as-markdown/blob/master/README.md)
- [Releases](https://github.com/yorkxin/copy-as-markdown/releases)
- [yorkxin/copy-as-markdown on GitHub](https://github.com/yorkxin/copy-as-markdown)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/yorkxin-copy-as-markdown
