Attach & share
Attach & share
The three commands that get media onto a PR, an issue, or a plain URL.
Attach files to a PR #
attach is the command you'll use most. From a checked-out branch with an open pull request, it needs no arguments beyond the files:
…which lands on the pull request as one managed comment:
📎 Attachments
Run attach again with new files and this same comment updates in place, so your PR doesn't collect a trail of stale screenshot comments.
It uses the gh CLI to find the current repo and PR. If you're not on a PR branch, or you want a different target, say so explicitly:
Want the URLs and ready-to-paste Markdown without touching any GitHub comment (say, to drop an image into a PR description yourself)?
Get a URL for any file #
put is the simpler building block: upload one file, get back a public URL and Markdown you can paste anywhere.
# re-encoded to WebP, then hosted at a stable public URL
>> optimized 411.5 KB → 94.2 KB (shot.webp)
URL: https://storage.uploads.sh/screenshots/app/2026-07-12/shot-9f2c1a.webp
MARKDOWN: A few useful variations:
By default, images are re-encoded to WebP (capped size, high quality) so GitHub embeds stay fast, and EXIF metadata is stripped. Use --keep-exif to keep metadata, or --no-optimize to skip all processing.
Each file also gets a shareable page on uploads.sh (under /f/…) that supports oEmbed — so chat apps, notes tools, and other unfurlers can embed the image when you paste the page link. Details are inReference.
Capture a screenshot #
Don't have the image yet? screenshot renders a URL or a local.html file and hosts the result in one step, with no browser install required.
# renders the page, then hosts the WebP in one step
>> captured via local backend
URL: https://storage.uploads.sh/gh/app/example/pull/123/app.example.webp
MARKDOWN: A few useful variations:
If a Chrome or Chromium is already on the machine, screenshot drives it directly (nothing to download); otherwise it renders on uploads.sh servers. Force either side with --via local or --via remote. Note thatlocalhost URLs are only reachable locally.
Shooting your own dev server, it hides known framework dev toolbars (Astro, Next, Nuxt, Vite) automatically (opt out with--no-hide-dev-tools) and takes --reduced-motion to settle animations. Hide any other overlay with --hide <selector>(repeatable), or run setup JS first with --eval <js> /--init-script <file> (local backend).