Desktop Pricing Docs Blog About GitHub Get started
← Back to blog

Paste-and-host, for your app

Everyone who's written a GitHub comment has used it: paste a screenshot straight into the box, and a second later it's a hosted image link. It's one of those tiny features you stop noticing because it just works. We rebuilt it as something you can drop into your own app.

The thing we're mimicking

GitHub's markdown editor has a quiet superpower. You paste an image from your clipboard — or drag a file onto the textarea — and it uploads in the background, dropping a ![](…) markdown link into the text where your cursor was. No file dialog, no "upload" button, no leaving the page. The screenshot you just took is a URL before you've finished your sentence.

That flow is the gold standard for getting an image out of your head and into a document. But it's first-party: it works because you're signed in to GitHub, and the image lands in GitHub's storage. If you're building your own tool — a CMS, a support desk, an internal wiki, an AI chat UI — you don't get it for free. You end up wiring a file input to a signed-upload endpoint to a storage bucket, and reinventing the placeholder-and-replace dance every time.

Why we built it

PixelVault started as an image host for AI agents and developers: upload via API, get a permanent CDN URL. But "get an image to a URL with as little friction as possible" is exactly the paste-and-host problem — and the people using our customers' apps are humans with clipboards, not just agents with API keys.

So we wanted to offer the GitHub flow as a feature other apps can embed. The catch: the whole reason GitHub's version is safe is that it's first-party. To hand it to a third-party site, we had to answer one uncomfortable question — how do you put an upload credential in someone's public HTML without it becoming a free-for-all?

Publishable keys

The answer is a second kind of API key, borrowed from the way payment providers split secret and publishable keys. A PixelVault publishable key (prefixed pv_pub_) is designed to live in browser source. It can do exactly one thing — upload an image — and nothing else. It can't list, read, or delete anything in your account.

On top of that, every publishable key carries an origin allowlist. An upload is only accepted if the browser's Origin header matches one of the origins you configured (say https://app.example.com). A key copied out of your page and used from somewhere else is simply rejected. Between "upload-only" and "origin-scoped," a leaked key is close to worthless — the worst case is someone uploading images from your own allowed site, which your normal rate limits and quotas already bound.

There's a nice consequence for the browser side: because the endpoint authenticates with a bearer key and never a cookie, there are no ambient credentials to steal, so cross-origin requests to the upload endpoint are safe to allow. The security boundary isn't CORS — it's the per-key origin check on the server.

How it works

The client side is small. A script listens for paste and drop on your textarea — plus an optional upload button for click-to-select — pulls any image out, and:

  • inserts an ![Uploading…]() placeholder at the caret (each with a unique token, so several uploads at once don't clobber each other);
  • POSTs the file to the PixelVault upload endpoint with the publishable key;
  • replaces that placeholder with ![name](https://img.pixelvault.dev/…) when the URL comes back — or a clear error if it doesn't.

That's the same insert-placeholder-then-swap trick GitHub uses, and it's what makes the flow feel instant even on a slow connection: your text stays editable while the upload runs.

The lightest way to add it is a single script tag. Every textarea you point it at becomes paste-and-host:

<script src="https://pixelvault.dev/paste.js"
        data-pv-key="pv_pub_xxxxxxxx"
        data-pv-target="textarea"></script>

If you're in a bundled app, install the framework-agnostic core instead and wire it up yourself (there are @pixelvault-dev/paste-react and @pixelvault-dev/paste-vue bindings too):

import { attachPaste } from "@pixelvault-dev/paste";

attachPaste(document.querySelector("textarea"), {
  publishableKey: "pv_pub_xxxxxxxx",
});

Both share one upload code path, so there's a single place where the placeholder logic, error handling, and markdown formatting live. You can override what gets inserted — HTML, BBCode, a bare URL — if markdown isn't your format.

How ours differs

We set out to copy GitHub's feel, not its constraints. Because the images land in your PixelVault project, they're yours: permanent, servable from your own domain, and every URL is a live transform endpoint — resize, crop, convert to WebP/AVIF, even remove a background, by adding query params. GitHub gives you a link; you get a link plus a small image pipeline behind it.

And it isn't only for people. The same uploads API powers our CLI, MCP server, and Claude Code skill, so an agent and a human pasting a screenshot end up in exactly the same place.

Try it

Create a publishable key in your dashboard (add the origins you'll embed it on), drop in the script tag, and your textareas host images. The docs have the full options reference.

Using an AI coding agent? Skip the wiring: our ready-made prompts (React, Next.js, Vue, TipTap, Lexical, Quill, plain HTML) add it for you — copy one, paste it into Claude Code, Cursor, or Copilot, and it wires up against our live API.

Free tier: 200 MB storage, 500 uploads/month, 1 GB bandwidth — no credit card. Read the docs →