Preview MDX files in Obsidian, with first-class support for Code Hike — scrollycoding, code annotations, focus lines, and compile-time syntax highlighting.
Forked from yulei-chen/obsidian-mdx and rewritten with a security-first architecture, mobile compatibility, and offline rendering.
Most MDX-related plugins for Obsidian only handle editing — they register .mdx as a plain-text file so Obsidian stops treating it as unknown, but they don't compile or render the MDX.
MDX Preview compiles your MDX so JSX and Code Hike annotations render in a live preview. Custom React components from your own app can't be resolved by the plugin, so they show a labeled placeholder rather than breaking the whole preview. Pair it with any edit-only plugin if you want richer editor support alongside the preview.
Why not the original MDX by yulei-chen? That plugin is the foundation this one was built on. This fork adds a security-first architecture (sandboxed iframe with a consent gate), bundles the renderer at build time so no internet connection is needed, and supports mobile.
!focus, !mark, !diff, and all Code Hike annotations work out of the box@code-hike/lighter highlighter is pure JavaScript with no native dependencies), so it works on iOS and Androidsandbox="allow-scripts" iframe with no access to your vault or Obsidian APIs/images/... paths backed by a nearby public/ folder, render in desktop and mobile preview.mdx files open directly in the preview view, no command palette step neededmain.js, manifest.json, and styles.css from the latest release..obsidian/plugins/mdx-preview/ inside your vault.Once listed, search for MDX Preview in Settings → Community Plugins → Browse and click Install.
.mdx extension — it opens automatically in the preview view.Copy this into a .mdx file to try Code Hike annotations:
export function Code({ codeblock }) {
return <pre>{codeblock.value}</pre>
}
## Annotated code
```js !focus
// !mark[/greet/] red
function greet(name) {
// !mark green
return `Hello, ${name}!`
}
```
For a full scrollycoding example, see the Code Hike vite example.
MDX is executable JavaScript. This plugin takes several steps to limit the blast radius:
sandbox="allow-scripts" with no allow-same-origin, giving it a null origin — vault files and Obsidian APIs are completely unreachable from inside the iframeeval() or new Function() is used — the compiled MDX function body is embedded directly as a <script> tag, which is the same model browsers use for normal scriptsdata: URLs — the only image form that loads in a null-origin sandbox (app:// resource URLs and host-created blob: URLs are both origin-scoped and are blocked there). Because the image bytes live in the same iframe as the MDX JavaScript, a script can read the bytes of any vault image the file names (including a path it guesses) and send them over the network. This does not expose arbitrary vault files — only images the previewed file explicitly references — but it is why the rule above holds: only preview files you trust.This repo uses pnpm (see pnpm-lock.yaml).
pnpm install
pnpm dev # esbuild --watch, builds main.js + styles.css with inline sourcemaps
pnpm build # tsc -noEmit type-check, then a minified production build
To see changes in Obsidian itself, symlink (or copy) manifest.json, main.js, and styles.css into a test vault at .obsidian/plugins/mdx-preview/, then reload Obsidian. Installing the community Hot-Reload plugin in that vault saves you from restarting Obsidian after every rebuild.
pnpm test # playwright test
tests/e2e/preview.spec.ts doesn't launch real Obsidian. It bundles src/renderer.tsx standalone with esbuild, compiles sample MDX through the same @mdx-js/mdx + codehike/mdx pipeline the plugin uses at runtime, and injects both into a sandboxed srcdoc iframe on a Playwright page, then asserts against the rendered DOM. This covers the renderer and MDX-compile pipeline in isolation — src/main.ts and src/mdxPreview.tsx (the Obsidian view wrapper) aren't exercised by these tests, so verifying those needs the manual vault loop above.
If Playwright reports a missing browser, run pnpm exec playwright install chromium once.
Issues and pull requests are welcome at jovialio/obsidian-mdx.
The decisions behind this plugin — mobile compatibility, offline rendering, eliminating eval(), and the sandboxed security model — are documented in detail:
From Fork to Production: How I Rebuilt an Obsidian MDX Plugin
Originally forked from yulei-chen/obsidian-mdx by yulei-chen. Thank you for the foundation.
MIT — see the LICENSE file for details.