README file from
GithubQMD as Markdown Obsidian Plugin
A plugin for Obsidian that allows seamless editing of QMD files as if they were Markdown.
QMD files combine Markdown with executable code cells and are supported by Quarto, an open-source publishing system. These files are compatible with editors like RStudio and VSCode.
This plugin originated in 2022 as a minimal change to a now-archived project by deathau: deathau/txt-as-md-obsidian. It has since evolved to include additional integrations and features.
As of the end of 2024, there are also other plugins exist that make it easier to work with Obsidian and Quarto:
- Ridian offers R code block execution and variable previews.
- Quarto Exporter helps export Obsidian Markdown files into the QMD format.
The main difference between this plugin and these other plugins is that this plugin allows you to compile QMD files as they are, without exporting them to another folder. In this regard, it is more similar to the Pandoc plugin.
Version History
0.1.0-rc.1 (beta — BRAT only)
- Added Render Quarto to PDF command and ribbon icon. Runs
quarto render <file>on the active.qmdfile (output format driven by the document's YAML —format: typstfor Typst,format: pdffor LaTeX). - Added "Open Compiled PDF in Obsidian" setting toggle. When enabled, the rendered PDF opens inside Obsidian using the built-in PDF viewer.
- PDF opens in a vertical split to the right of the source
.qmd, so the source tab is no longer replaced. - Re-running the render reuses the existing PDF tab (reloads the file in place) instead of stacking new tabs.
- Command exposes a
file-outputicon, so plugins like Commander can pin it to the toolbar.
0.0.3
- Added an option to run Quarto preview for the current
qmdfile.
0.0.2
- Repurposed the plugin to enable viewing and editing of QMD files.
- Made the plugin available via BRAT and Obsidian.
0.0.1
- Initial release by death_md, supporting
.txtfiles.
To-Do List
- Use Obsidian 1.8’s web preview to enable seamless in-app previews.
- Recognize
{language}for code block syntax highlighting. - Add CSS support for callout blocks.
- Enable the creation of new QMD files.
- Add a render command. (0.1.0-rc.1 — render to PDF, open inside Obsidian.)
Rendering to PDF (beta)
Available from 0.1.0-rc.1 via BRAT.
Three command-palette entries (all share the ribbon icon file-output, which is bound to the YAML-driven variant):
| Command | What it runs | When to use |
|---|---|---|
| Render Quarto (use YAML format) | quarto render <file> |
Document's YAML format: block decides the output. If YAML targets a non-PDF format (e.g. html, docx), the file still renders but Obsidian's built-in viewer will not open it — the plugin shows a path notice. |
| Render Quarto to PDF (Typst engine) | quarto render <file> --to typst |
Force the Typst engine regardless of YAML. Use QUARTO_TYPST setting to pin a Typst binary. |
| Render Quarto to PDF (LaTeX engine) | quarto render <file> --to pdf |
Force the LaTeX engine (lualatex/xelatex/pdflatex). |
The CLI flag --to pdf is Quarto's LaTeX path, not a generic "any PDF" — that's why the engine-specific commands are split out. Pick the YAML-driven one if your .qmd already declares the format you want; pick an explicit engine to override per-render without touching the file.
- Setting Open Compiled PDF in Obsidian (off by default):
- Off — render finishes, notice shows the PDF path. Open it however you want.
- On — rendered PDF opens in a vertical split on the right via Obsidian's built-in PDF viewer. Source tab keeps focus.
- Re-running the render reuses the existing PDF tab — no tab stacking.
- The
.qmdsource must live inside the vault (the rendered.pdflands next to it; Obsidian only opens vault files). - Custom
output-dirin_quarto.ymlis not yet handled — the plugin looks for<basename>.pdfnext to the source.
Enhancing Quarto File Integration in Obsidian
To enable linking with Quarto files, ensure the "Detect all file extensions" toggle is activated in the Files & Links section of Obsidian settings.
If you'd like to hide additional file types, use the following CSS snippet. Save it in your snippets folder and enable it via the Appearance menu in Obsidian. You can add more file extensions as needed.
div[data-path$='.Rproj'] {
display: none;
}
div[data-path$='.cls'] {
display: none;
}
div[data-path$='.yml'] {
display: none;
}
div[data-path$='.json'] {
display: none;
}
Compatibility
This plugin requires Obsidian v0.10.12 or later to work properly, as the necessary APIs were introduced in this version.
Installation
From Within Obsidian
The plugin is available in Obsidian's community plugin list. The community-store version always tracks the latest stable release (currently 0.0.3).
Beta releases via BRAT
Pre-release versions (-rc.x, -beta.x) are only distributed through BRAT. The community plugin store will not show them.
- Install Obsidian42 - BRAT from the community plugins list.
- Open BRAT settings → Add Beta plugin with frozen version is not needed — use Add Beta plugin.
- Enter the repo:
danieltomasz/qmd-as-md-obsidian. - BRAT reads
manifest-beta.jsonfrom the repo and installs the latest pre-release tag (e.g.0.1.0-rc.1). - Enable the plugin in Settings → Community plugins.
To switch back to stable, remove the plugin from BRAT and reinstall from the community store.
From GitHub
- Download the latest release from the Releases section of the GitHub repository.
- Extract the plugin folder from the zip file to your vault's plugins directory:
<vault>/.obsidian/plugins/- Note: On some systems, the
.obsidianfolder might be hidden. On macOS, pressCommand + Shift + Dotto reveal hidden folders in Finder.
- Note: On some systems, the
- Reload Obsidian.
- If prompted about Safe Mode, disable it and enable the plugin.
Alternatively, go to Settings → Third-party plugins, disable Safe Mode, and enable the plugin manually.
Security
Important: Third-party plugins can access files on your computer, connect to the internet, and install additional programs.
The source code for this plugin is open and available on GitHub for audit. While I assure you that the plugin does not collect data or perform any malicious actions, installing plugins in Obsidian always involves a level of trust.
Development
This project is built using TypeScript for type checking and documentation.
It relies on the latest Obsidian plugin API in TypeScript Definition format, which includes TSDoc comments for documentation.
Note: The Obsidian API is in early alpha and may change at any time.
To contribute or customize the plugin:
- Clone this repository.
- Run
npm ioryarnto install dependencies. - Use
npm run buildto compile the plugin. - Copy
manifest.json,main.js, andstyles.cssto a subfolder in your plugins directory:<vault>/.obsidian/plugins/<plugin-name>/ - Reload Obsidian to apply changes.
Alternatively, clone the repository directly into your plugins folder. After installing dependencies, run npm run dev to enable watch mode for live compilation.
Reload Obsidian (Ctrl + R) to view updates.
Make targets
The makefile wraps common tasks. Run make help for the list:
| Target | What it does |
|---|---|
make build |
Install deps (npm ci when package-lock.json exists, otherwise npm install), build main.js, then zip. |
make zip |
Bundle main.js + manifest.json into qmd-as-md.zip. |
make clean |
Wipe node_modules and build artefacts. |
make release-beta |
Publish a GitHub pre-release using the version in manifest-beta.json. |
make release-stable |
Publish a GitHub release using the version in manifest.json. |
Cutting a release
Two release channels share the same main branch:
- Stable —
manifest.jsonis the source of truth (e.g.0.0.3). Goes to the community plugin store. - Beta —
manifest-beta.jsonis the source of truth (e.g.0.1.0-rc.1). Distributed only via BRAT. Pre-release semver suffixes (-rc.x,-beta.x) are accepted by BRAT but rejected by the community store, so betas live exclusively here.
To publish a release:
# Beta — bump manifest-beta.json first, then:
make release-beta # interactive prompt for notes
make release-beta NOTES="Fixed leaf bug" # non-interactive
# Stable — bump manifest.json first, then:
make release-stable
Both targets:
- Check that
gh,node,npm, andzipare onPATH. - Read the version from the appropriate manifest, and refuse to overwrite an existing tag.
- Build
main.jsfresh (npm ciifpackage-lock.jsonexists, otherwisenpm install). - Create a GitHub release tagged with the version (no
vprefix — Obsidian convention) and attachmain.jsplus a correctly-versionedmanifest.json. The beta target stagesmanifest-beta.jsoninto a tempdir under the literal namemanifest.jsonso BRAT finds the asset it expects. - Mark beta releases as
--prerelease.
Requirements: gh authenticated against the repo, working tree clean.
After a beta release, BRAT users can hit Check for updates to all beta plugins to pull it.
Troubleshooting the release flow
Could not read version from manifest-beta.json / empty version
The recipe runs node -p "require('./manifest-beta.json').version" and treats an empty result as a hard error. Common causes:
- Wrong working directory. The recipe expects to be run from the repo root.
cdto it (pwdshould show the directory containingmanifest-beta.json) before runningmake. - Manifest missing or malformed. The recipe now prints the real Node.js error before bailing — read the message rather than the generic line.
ghnot authenticated for this repo. Rungh auth statusandgh repo set-default danieltomasz/qmd-as-md-obsidianif needed.
Variables silently lost between recipe lines (macOS default Make)
macOS ships GNU Make 3.81 (released 2006), which predates .ONESHELL (added in 3.82, 2010). On 3.81 the directive is silently ignored, so each recipe line runs in its own shell and any variable set on one line is gone by the next. The recipes in this repo are written as single \-joined shell invocations specifically to remain compatible with 3.81 — do not add .ONESHELL back without verifying the local make --version.
If you want a modern Make on macOS:
brew install make
gmake release-beta NOTES="…" # invoked as `gmake`, not `make`
BRAT says "this is not an Obsidian plugin"
BRAT walks the latest GitHub release looking for an asset named literally manifest.json. The previous version of this Makefile relied on gh release create's path#displayname rename syntax, which silently no-op'd in some gh CLI versions and uploaded the asset under its real basename (e.g. qmd-release-manifest.json) — invisible to BRAT. The current recipe sidesteps the rename mechanism by staging the file under the correct name in a tempdir before upload. If BRAT still rejects a release, inspect the assets:
gh release view <tag> | grep -i asset
The list must contain both main.js and manifest.json (exact spelling).
Release was created but gh release create failed silently
Older versions of the recipe used long \-joined chains without set -e. A failed npm install/npm run build or missing tool would let the chain continue past the failure, eventually erroring on gh release create with no clear cause. The current recipe sets set -e at the start of each release target, validates each tool with command -v, and verifies main.js was produced before invoking gh. If something still goes wrong, the first error surfaced by the recipe is the real one — read up, not down.