You clone a repo, double-click README.md, and Xcode starts up. Or TextEdit opens it as a wall of hashes and asterisks. Every AI tool you use now writes Markdown, and the Mac still treats it like a config file.
This is fixable in a few minutes, and once you see how macOS decides which app opens a file, the fix stays put. We'll go through it from the terminal, with the Finder version for anyone who'd rather click.
How macOS picks the app: one type, a few extensions
macOS doesn't map extensions to apps directly. It maps each file to a Uniform Type Identifier (UTI), and apps register as handlers for UTIs. Ask Spotlight what type a file is:
mdls -name kMDItemContentType README.md
# kMDItemContentType = "net.daringfireball.markdown"
We checked this on macOS 26.3: .md and .markdown both resolve to net.daringfireball.markdown, the type named after John Gruber's site, where Markdown started. Set a handler for that type and both extensions follow.
On a developer's Mac, several apps usually claim that type: Xcode, a couple of editors, whatever IDE you installed last. Until you pick one, macOS picks for you, and it's rarely the one you'd choose.
.mdown and .mkd are different. Unless an installed app declares them, macOS gives them dynamic types (dyn.ah62d4rv...), so they need their own handler. If your files use those extensions, renaming them to .md is the simplest fix.
Fix it with duti (two commands)
duti is a small command-line tool that sets default handlers by UTI. Install it with Homebrew:
brew install duti
Check what currently opens Markdown:
duti -x md
# Xcode.app
# /Applications/Xcode.app
# com.apple.dt.Xcode
Then set the handler by bundle ID. all covers every role (viewer, editor, shell):
duti -s <bundle-id> net.daringfireball.markdown all
To find any app's bundle ID:
defaults read "/Applications/Visual Studio Code.app/Contents/Info" CFBundleIdentifier
# com.microsoft.VSCode
So, for VS Code:
duti -s com.microsoft.VSCode net.daringfireball.markdown all
Run duti -x md again to confirm. Because it's one line, it fits in a dotfiles bootstrap script, so a new Mac gets the right default on day one:
# ~/dotfiles/macos/defaults.sh
duti -s com.microsoft.VSCode net.daringfireball.markdown all
duti -s com.microsoft.VSCode public.json all
duti -s com.microsoft.VSCode public.yaml all

Check the type, set the handler, confirm. The .mdown file is the one that needs its own default.
The Finder way
If you'd rather not install anything:
- Select any
.mdfile and press ⌘I (Get Info). - Expand Open with and pick the app.
- Press Change All, then confirm.
The step people skip is Change All. Picking an app without it only changes that one file, which is why the problem seems to come back.
Why it keeps reverting
-
An app re-registered on update. Xcode, in particular, can claim the type again after a big update. Re-run your
dutiline; this is where having it in a script pays off. - The file has its own override. A file you once set with "Open with" (without Change All) keeps that choice. Select it, Get Info, and press Change All from there.
- A stale Launch Services database. If nothing sticks, rebuilding the database usually clears it. It resets every file association on the Mac, so treat it as a last resort:
LSREGISTER=/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister
$LSREGISTER -kill -r -domain local -domain system -domain user
killall Finder
Reading without opening: Quick Look
A default app fixes double-clicking. Most of the time, though, you just want to read the file: a README in a repo you cloned, notes an AI agent left behind, release notes in a download.
That's Quick Look: select the file in Finder and press Space. Out of the box, macOS shows Markdown as raw source. A Quick Look extension can render it with headings, lists, tables and code blocks instead. To test whatever's installed from the terminal:
qlmanage -p README.md # preview a file the way Finder's Space does
pluginkit -mAv -p com.apple.quicklook.preview # list installed preview extensions
qlmanage -r && qlmanage -r cache # reset Quick Look if a new extension doesn't show up

Space in Finder with a Markdown Quick Look extension: headings, tables, task lists and code instead of raw text.
Where TypeFire fits
This part is ours, so here's exactly what it does. TypeFire is a Mac app that includes a Markdown Quick Look extension. Install the app and Space in Finder renders .md and .markdown files, math included ($x^2$ inline and $$ blocks). It's free, with nothing to switch on. If another Markdown previewer is installed, TypeFire tells you which one it replaced, and you can switch to another previewer in Settings → Markdown.
It also has a one-click default: Settings → Markdown → Set as Default makes TypeFire the default app for .md and .markdown files (under the hood, the same Launch Services call duti makes).

Once a default is set, every .md file in Finder carries that app's icon.
You don't need TypeFire for anything above; duti and Finder do the job. It's for anyone who wants rendered previews and an editor in one install. Our guide to the default Markdown editor on Mac has the Finder steps.
Here's opening and editing a .md from Finder in 85 seconds:
The short version
-
.mdand.markdownshare one type,net.daringfireball.markdown. Set the handler once. -
brew install duti, thenduti -s <bundle-id> net.daringfireball.markdown all. Put it in your dotfiles. - In Finder, the step that matters is Change All.
- For reading, use Quick Look (Space) with a Markdown extension, and test it with
qlmanage -p.
What opens your .md files right now? Run duti -x md and see.
Top comments (0)