In Notion, you press Cmd-V and the screenshot lands in your note. In Obsidian, same thing. In Org-mode, you press Cmd-V and get the clipboard text. The image is still sitting in your screenshot buffer, uninserted, wondering what it did wrong.
Emacs can do this. It just needs to be told how.
The problem
You take a screenshot with Cmd-Shift-4 on macOS. It’s on the clipboard. You want it in your Org file — inline, visible, right where you’re typing. Org-mode doesn’t do this out of the box. There’s no built-in “paste image from clipboard” command.
This matters because screenshots are how I think. Architecture diagrams, terminal output, UI bugs, Slack conversations I want to annotate later. If pasting an image requires opening Finder, dragging a file, and manually writing a link — I won’t do it. The friction kills the workflow.
The solution: org-download + pngpaste
org-download is the package. It handles saving clipboard images to disk and inserting the Org link. On macOS, it needs pngpaste as the backend to read the clipboard.
Install pngpaste:
brew install pngpaste
Then configure org-download:
(use-package org-download
:after org
:defer nil
:custom
(org-download-method 'directory)
(org-download-image-dir "images")
(org-download-heading-lvl nil)
(org-download-timestamp "%Y%m%d-%H%M%S_")
(org-image-actual-width 300)
(org-download-screenshot-method "/usr/local/bin/pngpaste %s")
:bind
("C-M-y" . org-download-screenshot)
:config
(require 'org-download))
What each setting does:
| Setting | What it does |
|---|---|
org-download-method 'directory |
Save images to a directory (not alongside the org file) |
org-download-image-dir "images" |
Save to ./images/ relative to the org file |
org-download-heading-lvl nil |
Don’t create subdirectories per heading |
org-download-timestamp |
Prefix filenames with YYYYMMDD-HHMMSS_ |
org-image-actual-width 300 |
Display inline images at 300px width |
org-download-screenshot-method |
Use pngpaste to read macOS clipboard |
Now C-M-y pastes whatever’s on the clipboard as a PNG file into ./images/ and inserts the link.
The gotcha: relative paths break in roam
This setup works great until you use org-roam. The "images" directory is relative — it resolves to ./images/ next to whatever org file you’re editing. That’s fine when all your notes live in one folder.
But org-roam notes move around. You refactor. You reorganize subdirectories. A note that lived in roam/notes/ moves to roam/work/. Now its image links point to roam/work/images/, but the actual files are still in roam/notes/images/. Every image breaks.
The fix: make the image directory absolute.
(setq-default org-download-image-dir "~/zorg/roam/images")
All screenshots land in one canonical directory. Notes can move freely between subdirectories and images still resolve. Took me months to figure out why images kept breaking after reorganizations.
Seeing inline images
By default, Org-mode shows image links as text: [[./images/20240629-143022_screenshot.png]]. The actual image is just a file link. To see it rendered inline, you toggle inline image display.
The command: org-toggle-inline-images, bound to C-c C-x C-v in vanilla Org. In my config it’s zi (evil-mode shortcut — toggle images).
A few things about inline image display:
- It’s a buffer-local toggle. Turning it on in one buffer doesn’t affect others.
- It reads
org-image-actual-widthto determine display size. Without this, images render at full resolution and your buffer becomes a gallery wall. - It only works for local files. Remote URLs need to be downloaded first. org-download handles this — if you yank a URL, it’ll download the image and create a local copy.
- It can slow down large files. If you have a note with 50 inline images, toggling takes a moment. Not a deal-breaker, but noticeable.
For startup behavior, you can auto-enable inline images:
(setq org-startup-with-inline-images t)
I don’t do this — I prefer explicit toggling. Most of my notes are text-heavy, and inline images add visual noise when I’m in writing mode.
Other ways to get images in
org-download is the clipboard workflow. There are others.
Drag and drop
Emacs 29+ supports drag-and-drop of files into buffers. With org-download loaded, dropping an image file onto an Org buffer will copy it to the image directory and insert the link. Same behavior as the clipboard paste, just triggered by dragging from Finder instead.
This requires (org-download-enable) in your config, which the use-package :config block in the setup above handles via (require 'org-download).
org-attach
org-attach is Org’s built-in file attachment system. It stores files in a per-heading directory (usually under data/), keyed by the heading’s UUID. Use C-c C-a to access the attach dispatcher.
The difference from org-download:
| org-download | org-attach | |
|---|---|---|
| Storage | One shared directory | Per-heading UUID directories |
| Use case | Screenshots, quick images | Any file attachment |
| Image display | Inline via toggle | Inline (with some config) |
| File naming | Timestamp-based | Original filename |
| Portability | Simple paths | UUID-based, more robust |
I use org-download for screenshots (fast, visual) and org-attach for everything else (PDFs, data files, archives). They coexist without conflict.
Manual links
The low-tech approach: save the image yourself, write the link manually.
[[file:~/zorg/roam/images/my-diagram.png]]
Works. Not exciting. Useful when you’re linking to an image that already exists somewhere rather than pasting a new one.
Making it smooth on macOS
A few things I learned the hard way:
pngpaste path matters. The config uses /usr/local/bin/pngpaste (Intel Homebrew). If you’re on Apple Silicon, it’s /opt/homebrew/bin/pngpaste. Or just use "pngpaste %s" and let your $PATH resolve it — but only if Emacs inherits your shell PATH (see exec-path-from-shell).
Screenshot region to clipboard. Cmd-Ctrl-Shift-4 captures a region directly to clipboard without saving a file to Desktop. Then C-M-y in Emacs to paste. Two keystrokes, screenshot in your note.
Retina scaling. macOS screenshots are 2x resolution on Retina displays. Setting org-image-actual-width 300 keeps them from eating your entire screen when displayed inline. Adjust to taste.
Emacs Plus or railwaycat. GUI Emacs on macOS needs a proper build to handle clipboard images well. emacs-plus via Homebrew or railwaycat’s build both work. The terminal version can’t display inline images at all (obviously).
The full workflow
My daily workflow looks like this:
Cmd-Ctrl-Shift-4— screenshot region to clipboard- Switch to Emacs, navigate to the right heading
C-M-y— org-download pastes and inserts the linkzi— toggle inline images to see it
Four steps. About three seconds. Comparable to Notion, honestly, once the config is in place.
The setup took an afternoon. The roam fix took months of confused image breakage before I diagnosed it. But now it works, and I don’t think about it anymore — which is the whole point of good tooling.