TLDR: Obsidian's markdown flavor (
![[image.png]],[[wikilinks]]) doesn't render on the web. A small translation layer fixes it, and suddenly your notes app IS your CMS.
the setup
I was rebuilding my personal site from scratch in Next.js 16 (React-based web framework, deploys to Vercel, my hosting platform).
19 posts. 20 image attachments. All sitting on pika.page, my old dead-simple publishing platform, and I wanted them home.
Every one of those posts started as a note in Obsidian (my notes and PKM app — personal knowledge management, basically a supercharged local markdown vault with graph-style backlinks between ideas).
So the move felt obvious: write in Obsidian, publish to the web. Simple, right?
the wall
Here's what nobody tells you.
Obsidian has its OWN flavor of markdown.
![[image.png]] is how you embed an image inside Obsidian. Drop that in front of a standard web renderer? It just prints the literal text. Broken. Useless.
[[some note title]] is a wiki-link. It's the backbone of how Obsidian connects ideas. A standard markdown library has absolutely no idea what to do with it.
And the image attachment paths? Obsidian stores them relative to your vault structure. A Next.js app expects them in public/. Completely different worlds.
I pointed a standard renderer at my Obsidian files and got garbage output. Angle brackets. Raw syntax showing through. Images 404ing.
what I tried first
My first move was "there HAS to be a library for this."
There are some. None of them matched exactly how I wanted attachments handled, and I kept hitting edge cases my posts exposed.
I'll admit it: I spent longer hunting for a ready-made answer than it would have taken to just write the thing myself. Classic trap.
the fix that worked
Three files. That's genuinely it.
lib/obsidian-md.ts— transforms Obsidian-flavored markdown into standard web-renderable HTML: converts![[...]]embeds, resolves[[wikilinks]], cleans up frontmatterlib/render.ts— the final rendering pass that takes the transformed output and makes it page-readyscripts/copy-attachments— a build-time script that walks the vault, finds every image, and copies it intopublic/so paths actually resolve
Commit efb0d36. Nineteen posts live. Twenty images following along for the ride.
One bonus find along the way: Next.js 16 changed the default behavior of dynamicParams, and it was quietly exposing draft posts to anyone who guessed the URL. Caught it before anything went live. One config line to close it.
Worth knowing if you're on Next 16.
why this matters to me
Obsidian is GENIUS for writing. The backlinks, the graph view, the way a note about one project connects to a half-finished thought from three months ago. Nothing else does that the same way.
But the moment I wanted to SHARE something, I'd have to copy-paste into another tool, reformat by hand, and pray the images came with me.
This pipeline ended that loop entirely. I write in Obsidian the same way I always have. The translation layer is invisible. The posts just… appear.
If your writing already lives somewhere you love, don't abandon the tool. Build the bridge instead.
P.S. All 19 posts migrated from pika.page came through clean on the first run. That genuinely never happens. I'm still suspicious.