** DONE Potential Markdown Data Loss When You Will Move Away from Obsidian :blog:pim:standards: CLOSED: [2026-04-08 Wed 23:11] :PROPERTIES: :ID: 2026-04-08-obsidian-md-portability :CREATED: [2026-04-08 Wed 23:04] :END: :LOGBOOK: - State "DONE" from "TODO" [2026-04-08 Wed 23:11] :END: This report is extending [[id:2025-05-10-Markdown-disaster][my general be-careful-with-Markdown article]] because users of Obsidian in particular do seem to ignore warnings about potential conversion loss for their future tool switch when Obsidian does not meet their requirements any more or is not available any longer - as so many closed source tools did before. This is a report generated by Claude.ai Opus 4.6 on [2026-04-08 Wed] for the prompt: #+BEGIN_QUOTE Please do generate a report on Markdown issues that will or may arise when a person moves away data from Obsidian. In general, Markdown has many different flavors and tools like Obsidian or Obsidian add-ons introduce new syntax elements that are not understood by other tools such as pandoc. In this example, I would suggest to convert Obsidian files to HTML that we do have a concrete target syntax example which is a valid choice for that experiment. Suggest a different format if you do have good arguments for a different target format. Do differ between standard Obsidian Markdown syntax elements (the focus) and popular addons (to get likely issues when not just sticking to the default elements). Add syntax examples for each case with the Markdown source (Markdown blocks) and the HTML result (HTML blocks) so that it is visible in what respect syntax information got lost or not handled properly as you would like to expect for a perfect data conversion. Also add a list of dynamic Obsidian features that are not available outside of Obsidian automatically when you are re-using the Markdown files elsewhere such as node visualizations and such. Bonus is when you can also come up with a list of Obsidian Markdown elements that are not part of the CommonMark syntax element list. #+END_QUOTE So if you do use Obsidian and find something here which is not accurate or changed over time, I would ask you to leave a comment so that I might fix that error. *I did not check all those findings myself* and LLMs like Claude may hallucinate here or there. The LLM output follows now: *** 1. Introduction [[https://obsidian.md/][Obsidian]] stores notes as plain [[https://en.wikipedia.org/wiki/Markdown][Markdown]] files --- but "plain" is misleading. Obsidian uses what it calls *Obsidian Flavored Markdown*, a combination of CommonMark, GitHub Flavored Markdown (GFM), and a set of proprietary syntax extensions. On top of that, the Obsidian plugin ecosystem introduces further non-standard syntax that only renders inside Obsidian itself. This report documents the specific syntax elements that cause problems when you try to move your Obsidian vault to another tool or convert files to HTML (or another format). Each section provides the *Obsidian Markdown source*, the *expected ideal HTML output*, and the *actual output* you would typically get from a standard converter like [[https://pandoc.org/][pandoc]]. **** Why HTML as the target format? HTML is a good test target because: - It is a well-defined, standards-based format with no ambiguity. - Pandoc (the most common Markdown-to-anything converter) supports it natively. - It makes information loss immediately visible --- you can see what rendered and what didn't. - Most other target formats (PDF, DOCX, EPUB) go through an HTML-like intermediate step anyway. That said, if your goal is long-term archival rather than conversion testing, *HTML with embedded CSS* or *EPUB* are both strong choices. If you want to stay in the Markdown ecosystem but on a more standardized footing, converting to *strict CommonMark* (stripping Obsidian extensions) is another valid path --- though you will lose features. *** 2. Standard Obsidian Syntax: What Breaks These are features built into Obsidian core --- no plugins required. **** 2.1 Wikilinks (Internal Links) Obsidian's signature feature. Wikilinks use double-bracket syntax to link notes by name, without requiring a file path or extension. *Markdown source:* #+begin_src markdown See [[My Other Note]] for details. Link with alias: [[My Other Note|click here]] Link to heading: [[My Other Note#Section Two]] Link to block: [[My Other Note#^abc123]] #+end_src *Expected ideal HTML:* #+begin_src html

See My Other Note for details.

Link with alias: click here

Link to heading: Section Two

Link to block: abc123

#+end_src *Actual pandoc output (default settings):* #+begin_src html

See [[My Other Note]] for details.

Link with alias: [[My Other Note|click here]]

Link to heading: [[My Other Note#Section Two]]

Link to block: [[My Other Note#^abc123]]

#+end_src *What went wrong:* Pandoc does not recognize wikilinks by default. The double brackets appear as literal text. Pandoc 3.0+ added =wikilinks_title_after_pipe= and =wikilinks_title_before_pipe= extensions, but they still do not handle image wikilinks, block references (=^id=), or vault-relative path resolution. **** 2.2 Embeds (Transclusion) Obsidian can embed the content of another note, a section of a note, or media files inline. *Markdown source:* #+begin_src markdown ![[Meeting Notes]] ![[Meeting Notes#Decisions]] ![[Meeting Notes#^block-ref]] ![[photo.png]] ![[photo.png|300]] ![[document.pdf]] ![[recording.mp3]] #+end_src *Expected ideal HTML:* #+begin_src html

Content of Meeting Notes would appear here...

Decisions

Decision content here...

The specific referenced paragraph...

photo photo #+end_src *Actual pandoc output:* #+begin_src html

![[Meeting Notes]]

![[Meeting Notes#Decisions]]

![[Meeting Notes#^block-ref]]

![[photo.png]]

![[photo.png|300]]

![[document.pdf]]

![[recording.mp3]]

#+end_src *What went wrong:* Pandoc treats all embeds syntax elements as literal text. Note transclusion is an inherently dynamic, Obsidian-specific feature --- no static converter resolves it. Image embeds with the wikilink syntax and the =|width= size hint are also not recognized. Even with pandoc's wikilink extensions enabled, image wikilinks remain unprocessed. **** 2.3 Block References and Block IDs Obsidian allows you to assign an ID to any paragraph or list item, then link or embed that specific block from elsewhere. *Markdown source:* #+begin_src markdown This is an important conclusion. ^my-conclusion - Item A - Item B - Item C ^my-list Link to the block: [[Note#^my-conclusion]] #+end_src *Expected ideal HTML:* #+begin_src html

This is an important conclusion.

Link to the block: my-conclusion

#+end_src *Actual pandoc output:* #+begin_src html

This is an important conclusion. ^my-conclusion

^my-list

Link to the block: [[Note#^my-conclusion]]

#+end_src *What went wrong:* The =^block-id= markers are rendered as literal text. They are not converted to HTML =id= attributes. The linking syntax also fails because wikilinks are not recognized. **** 2.4 Callouts Obsidian extends blockquote syntax with a type marker to create styled callout boxes. *Markdown source:* #+begin_src markdown > [!warning] Be careful > This action cannot be undone. > [!tip]+ Expand me > This is expanded by default. > [!faq]- Collapsed section > Hidden until the user clicks. > [!note] > A plain note callout without custom title. #+end_src *Expected ideal HTML:* #+begin_src html
Be careful

This action cannot be undone.

Expand me

This is expanded by default.

Collapsed section

Hidden until the user clicks.

Note

A plain note callout without custom title.

#+end_src *Actual pandoc output:* #+begin_src html

[!warning] Be careful This action cannot be undone.

[!tip]+ Expand me This is expanded by default.

[!faq]- Collapsed section Hidden until the user clicks.

[!note] A plain note callout without custom title.

#+end_src *What went wrong:* Pandoc renders these as ordinary blockquotes. The =[!type]= marker, the custom title, and the collapsible modifiers all appear as literal text inside the quote. The semantic meaning (warning vs.Β tip vs.Β note), the visual styling, and the interactive collapse/expand behavior are entirely lost. **** 2.5 Highlight Syntax Obsidian supports ===text=== for highlighted (marked) text. *Markdown source:* #+begin_src markdown This is ==highlighted text== in a sentence. #+end_src *Expected ideal HTML:* #+begin_src html

This is highlighted text in a sentence.

#+end_src *Actual pandoc output (default Markdown reader):* #+begin_src html

This is ==highlighted text== in a sentence.

#+end_src *Workaround:* Pandoc's =markdown+mark= extension does support this, but it must be explicitly enabled: =pandoc -f markdown+mark=. Without it, the ==== delimiters appear literally. **** 2.6 Comments (Obsidian-Specific) Obsidian uses =%%= to delimit comments that are invisible in reading/preview mode but present in the source. *Markdown source:* #+begin_src markdown Visible text. %% This is a private comment that should not appear in output. %% More visible text. Inline comment: some text %% hidden note %% continues here. #+end_src *Expected ideal HTML:* #+begin_src html

Visible text.

More visible text.

Inline comment: some text continues here.

#+end_src *Actual pandoc output:* #+begin_src html

Visible text.

%% This is a private comment that should not appear in output. %%

More visible text.

Inline comment: some text %% hidden note %% continues here.

#+end_src *What went wrong:* The =%%= comment syntax is completely Obsidian-specific. Pandoc and every other Markdown processor treats =%%= as literal text, exposing your private annotations in the output. **** 2.7 YAML Frontmatter / Properties Obsidian uses YAML frontmatter for metadata (title, tags, aliases, dates, custom fields). This is also used by other tools, so support varies. *Markdown source:* #+begin_src markdown --- title: Project Status tags: - project - quarterly aliases: - Q3 Status - Project Update cssclasses: - wide-page date: 2025-06-15 --- # Project Status Content begins here. #+end_src *Expected ideal HTML:* #+begin_src html

Project Status

Content begins here.

#+end_src *Actual pandoc output:* #+begin_src html

Project Status

Content begins here.

#+end_src *Status:* Pandoc handles standard YAML frontmatter well --- it strips it from output and can use fields like =title= and =date=. However, Obsidian-specific fields like =aliases=, =cssclasses=, and nested tag structures have no meaning outside Obsidian and are silently discarded. This is not a rendering failure, but it is *metadata loss* --- your aliases and custom properties vanish. **** 2.8 Tags (Inline) Obsidian supports inline tags with =#= and nested tags with =/=. *Markdown source:* #+begin_src markdown This note is about #machine-learning and specifically #ml/transformers. Nested tags like #project/alpha/phase-2 are common. #+end_src *Expected ideal HTML:* #+begin_src html

This note is about #machine-learning and specifically #ml/transformers.

Nested tags like #project/alpha/phase-2 are common.

#+end_src *Actual pandoc output:* #+begin_src html

This note is about #machine-learning and specifically #ml/transformers.

Nested tags like #project/alpha/phase-2 are common.

#+end_src *What went wrong:* Inline tags survive as plain text (which is acceptable for readability), but their semantic meaning as navigable, filterable tags is entirely lost. The hierarchical tag structure (=ml/transformers= as a child of =ml=) has no equivalent in HTML. **** 2.9 Math / LaTeX Obsidian supports inline and block LaTeX math via =$...$= and =$$...$$=, using MathJax/KaTeX. *Markdown source:* #+begin_src markdown Inline math: $E = mc^2$ Block math: $$ \int_{0}^{\infty} e^{-x^2} dx = \frac{\sqrt{\pi}}{2} $$ #+end_src *Expected ideal HTML (with MathJax):* #+begin_src html

Inline math: \(E = mc^2\)

\[\int_{0}^{\infty} e^{-x^2} dx = \frac{\sqrt{\pi}}{2}\]

#+end_src *Actual pandoc output:* #+begin_src html

Inline math: E = mc2

#+end_src *Status:* Pandoc handles math reasonably well with =--mathjax= or =--katex= flags. Without those flags, it attempts to render math as plain HTML, which works for simple expressions but breaks for complex LaTeX. The key issue is that you *must* explicitly configure your converter for math support --- it is not automatic. **** 2.10 Mermaid Diagrams Obsidian renders Mermaid diagram code blocks natively. *Markdown source:* #+begin_src markdown ```mermaid graph LR A[Start] --> B{Decision} B -->|Yes| C[Action] B -->|No| D[End] ``` #+end_src *Expected ideal HTML:* #+begin_src html
graph LR A[Start] --> B{Decision} B -->|Yes| C[Action] B -->|No| D[End]
#+end_src *Actual pandoc output:* #+begin_src html
graph LR
    A[Start] --> B{Decision}
    B -->|Yes| C[Action]
    B -->|No| D[End]
#+end_src *What went wrong:* Pandoc outputs the Mermaid code as a regular code block. Without a post-processing step that adds the Mermaid JavaScript library and changes the tag structure, you get raw diagram source code instead of a rendered diagram. **** 2.11 Footnotes Obsidian supports standard footnote syntax --- this is actually well-supported. *Markdown source:* #+begin_src markdown This claim needs a source[^1]. [^1]: Smith, J. (2024). *Important Research*. Journal of Examples. #+end_src *Expected ideal HTML:* #+begin_src html

This claim needs a source1.

  1. Smith, J. (2024). Important Research. Journal of Examples.β†©οΈŽ

#+end_src *Actual pandoc output:* Pandoc handles footnotes correctly. This is one area where conversion works well. **** 2.12 Image Resize Syntax Obsidian extends standard Markdown image syntax with a pipe-based size hint. *Markdown source:* #+begin_src markdown ![Alt text|300](image.png) ![Alt text|640x480](image.png) #+end_src *Expected ideal HTML:* #+begin_src html Alt text Alt text #+end_src *Actual pandoc output:* #+begin_src html
Alt text|300
Alt text|300
#+end_src *What went wrong:* Pandoc interprets =|300= as part of the alt text. The image renders at its original size, and the alt text and caption include the raw size value. *** 3. Popular Plugin Syntax: Additional Breakage These syntax elements are introduced by widely-used community plugins. **** 3.1 Dataview Queries Dataview is the most popular Obsidian plugin. It treats your vault as a queryable database. *Markdown source:* #+begin_src markdown ```dataview TABLE file.ctime AS "Created", file.tags AS "Tags" FROM #project SORT file.ctime DESC ``` ```dataviewjs dv.table(["Name", "Status"], dv.pages("#task") .map(p => [p.file.link, p.status]) ) ``` Inline query: `= this.file.name` #+end_src *Expected ideal HTML:* #+begin_src html
FileCreatedTags
Note A2025-01-15#project
#+end_src *Actual pandoc output:* #+begin_src html
TABLE file.ctime AS "Created", file.tags AS "Tags"
FROM #project
SORT file.ctime DESC
dv.table(["Name", "Status"],
  dv.pages("#task")
    .map(p => [p.file.link, p.status])
)

Inline query: = this.file.name

#+end_src *What went wrong:* Dataview queries are dynamic --- they execute at render time inside Obsidian and produce results from your vault's metadata. Outside Obsidian, they are just inert code blocks. There is no way to convert these statically without first running the queries inside Obsidian and serializing the results to Markdown (the Dataview Serializer plugin does this). **** 3.2 Templater Syntax The Templater plugin adds a template language with =<% ... %>= delimiters. *Markdown source:* #+begin_src markdown Created: <% tp.date.now("YYYY-MM-DD") %> Author: <% tp.file.title %> Previous: <% tp.date.now("YYYY-MM-DD", -1) %> <%* if (tp.file.tags.includes("#meeting")) { %> ## Attendees <%* } %> #+end_src *Expected ideal HTML:* #+begin_src html

Created: 2025-06-15

Author: My Note Title

Previous: 2025-06-14

Attendees

#+end_src *Actual pandoc output:* #+begin_src html

Created: <% tp.date.now("YYYY-MM-DD") %>

Author: <% tp.file.title %>

Previous: <% tp.date.now("YYYY-MM-DD", -1) %>

<%* if (tp.file.tags.includes("#meeting")) { %>

Attendees

<%* } %>

#+end_src *What went wrong:* Templater expressions are escaped as literal text. If you export a note /before/ Templater has processed it (e.g., a template file rather than a rendered note), the raw template syntax appears in the output. If Templater already ran and replaced the expressions with values, there is no issue --- the note contains plain Markdown. **** 3.3 Tasks Plugin Extended Syntax The Tasks plugin extends standard checkbox syntax with dates, priorities, and recurrence. *Markdown source:* #+begin_src markdown - [ ] Review PR #42 πŸ“… 2025-06-20 ⏫ - [x] Deploy hotfix βœ… 2025-06-14 πŸ“… 2025-06-15 - [ ] Weekly standup πŸ” every Monday πŸ“… 2025-06-17 - [ ] Write documentation ⏳ 2025-06-18 βž• 2025-06-10 #+end_src *Expected ideal HTML:* #+begin_src html #+end_src *Actual pandoc output:* #+begin_src html #+end_src *What went wrong:* The checkboxes render correctly (GFM task lists are well-supported), but the emoji-based date markers, priority symbols, and recurrence rules appear as literal emoji characters. Their semantic meaning --- due dates, scheduling, priority levels --- is lost. The tasks are no longer queryable or filterable. **** 3.4 Kanban Board Plugin The Kanban plugin stores board data as a specially structured Markdown file. *Markdown source:* #+begin_src markdown --- kanban-plugin: basic --- ## To Do - [ ] Design mockups - [ ] Write user stories ## In Progress - [ ] API development ## Done - [x] Requirements gathering #+end_src *Expected ideal HTML:* #+begin_src html

To Do

Design mockups
Write user stories
#+end_src *Actual pandoc output:* #+begin_src html

To Do

In Progress

#+end_src *What went wrong:* The content is there, which is good --- the headings and task items survive. But the visual kanban layout (multi-column board with drag-and-drop) is entirely lost. What you get is a flat list under headings. The =kanban-plugin: basic= frontmatter key is silently discarded. **** 3.5 Excalidraw Drawings The Excalidraw plugin stores drawings as =.excalidraw.md= files --- these are Markdown files with embedded JSON. *Markdown source (simplified):* #+begin_src markdown --- excalidraw-plugin: parsed --- # Drawing %% ## Drawing ```json { "type": "excalidraw", "elements": [ {"type": "rectangle", "x": 10, "y": 10, "width": 200, "height": 100} ] } #+end_src *Expected ideal HTML:* #+begin_example Excalidraw drawing #+end_example *Actual pandoc output:* The file structure confuses most processors. You get a mix of literal =%%= markers, raw JSON, and broken formatting. The drawing is completely unrenderable. *Mitigation:* Excalidraw can auto-export drawings as PNG or SVG files. If this setting was enabled, you can reference the exported images instead. **** 3.6 Advanced Slides (reveal.js) Syntax The Advanced Slides plugin uses =---= as slide separators and adds speaker notes and other directives. *Markdown source:* #+begin_src markdown # Presentation Title --- ## Slide Two Content here. note: These are speaker notes not visible to the audience. --- ## Red Slide With a colored background. #+end_src *Expected ideal HTML:* #+begin_src html

Presentation Title

Slide Two

Content here.

Red Slide

With a colored background.

#+end_src *Actual pandoc output:* #+begin_src html

Presentation Title


Slide Two

Content here.

note: These are speaker notes not visible to the audience.


Red Slide

With a colored background.

#+end_src *What went wrong:* The =---= separators become =
= elements. Speaker notes appear as regular paragraphs. The HTML comment directive for slide backgrounds passes through unchanged but has no effect without reveal.js. *** 4. Dynamic Obsidian Features Lost Outside the App These features are not just syntax elements --- they are runtime behaviors of the Obsidian application that have no equivalent in static files. | Feature | What It Does | Why It Cannot Be Exported | |----------------------------------+-----------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------| | *Graph View* | Interactive node visualization showing connections between all notes via links and tags | Requires real-time analysis of the entire vault's link structure; no static equivalent | | *Backlinks Panel* | Shows which other notes link to the current note | Requires vault-wide indexing; a static file has no awareness of other files linking to it | | *Outgoing Links Panel* | Shows all links from the current note and whether they resolve | Same vault-awareness problem | | *Search* | Full-text and metadata search across the vault | Requires an index or search engine; not a property of individual files | | *Tag Pane / Tag Navigator* | Browse and filter notes by tag, including nested tag hierarchy | Tags are only meaningful with a vault-wide index | | *Local Graph* | A zoomed-in graph showing only the current note's direct connections | Subset of graph view; same limitations | | *Starred / Bookmarked Notes* | Quick access list configured per-vault | Stored in Obsidian's app config (=.obsidian/=), not in the Markdown files | | *Daily Notes / Periodic Notes* | Automatic creation of date-based notes from templates | A workflow feature of the app; the resulting Markdown files are just normal notes | | *Quick Switcher* | Fuzzy-find navigation between notes | An app feature; files have no concept of this | | *Canvas* | Visual spatial arrangement of notes, images, and cards on an infinite canvas | Stored as =.canvas= JSON files; requires Obsidian's canvas renderer | | *Hover Preview* | Preview note content by hovering over an internal link | An app interaction; not representable in HTML without complex JavaScript | | *Live Preview / WYSIWYG editing* | Rendered Markdown editing mode | An editor feature, not a file format property | | *Command Palette* | Extensible command system | App functionality | | *Workspace Layouts* | Saved pane and tab arrangements | Stored in =.obsidian/workspaces.json= | | *Publish* | Obsidian's hosted publishing service | A separate product/service, not a file-level feature | | *Sync* | End-to-end encrypted sync across devices | A service, not a file feature | *** 5. Obsidian Markdown Elements Not in CommonMark The following is a summary of syntax elements that Obsidian supports but that are *not part of the CommonMark specification*. Some come from GFM, some from other extensions, and some are Obsidian-only. **** From GitHub Flavored Markdown (GFM), not in CommonMark: | Element | Syntax | CommonMark? | GFM? | |-------------------------+-----------------------+-------------+------| | Tables | =\| col \| col \|= | No | Yes | | Task lists (checkboxes) | =- [ ] task= | No | Yes | | Strikethrough | =~~text~~= | No | Yes | | Autolinked URLs | =https://example.com= | No | Yes | **** Obsidian-specific (not in CommonMark or GFM): | Element | Syntax | Notes | |----------------------------+-------------------------------------+-------------------------------------------------------| | Wikilinks | =[[Note]]=, =[[Note\|alias]]= | Obsidian-only; pandoc 3+ has partial support | | Embeds / Transclusion | =![[Note]]=, =![[image.png]]= | Obsidian-only | | Block IDs | =^block-id= appended to paragraphs | Obsidian-only | | Block references | =[[Note#^block-id]]= | Obsidian-only | | Callouts | => [!type] Title= | Obsidian-specific; 5 types overlap with GitHub alerts | | Collapsible callouts | => [!type]+= / => [!type]-= | Obsidian-only | | Highlight | ===text=== | Not CommonMark; supported by some processors | | Comments | =%% comment %%= | Obsidian-only | | Image resize (wikilink) | =![[img.png\|300]]= | Obsidian-only | | Image resize (Markdown) | =![alt\|300](url)= | Obsidian-only | | Nested / hierarchical tags | =#tag/subtag/subsubtag= | Obsidian-only | | YAML aliases property | =aliases: [...]= in frontmatter | Obsidian-specific meaning | | YAML cssclasses property | =cssclasses: [...]= in frontmatter | Obsidian-specific meaning | | =obsidian://= URI scheme | =[link](obsidian://open?vault=...)= | Obsidian app protocol | | Inline math | =$E=mc^2$= | Not CommonMark (common extension) | | Block math | =$$...$$= | Not CommonMark (common extension) | | Mermaid code blocks | =```mermaid= | Not CommonMark (common extension) | | Footnotes | =[^1]= / =[^1]:= | Not CommonMark (common extension) | *** 6. Recommendations for Migration If you are planning to move away from Obsidian, here are practical steps to minimize data loss: 1. *Audit your syntax usage.* Search your vault for =[[=, =![[=, => [!=, =%%=, =^=, ====, and =dataview= to find Obsidian-specific syntax. 2. *Convert wikilinks before exporting.* The Obsidian Link Converter plugin can batch-convert =[[wikilinks]]= to standard =[text](file.md)= Markdown links. 3. *Serialize dynamic content.* If you use Dataview, install the Dataview Serializer plugin to bake query results into the Markdown files before export. 4. *Export Excalidraw as images.* Enable Excalidraw's auto-export to SVG or PNG so you have static versions of your drawings. 5. *Strip or convert comments.* Do a find-and-replace for =%%...%%= blocks --- either delete them or convert to HTML comments (==). 6. *Use pandoc with extensions.* The command =pandoc -f markdown+wikilinks_title_after_pipe+mark --mathjax= covers wikilinks (partially), highlights, and math. 7. *Process callouts with a filter.* Use a pandoc Lua filter or a preprocessing script to convert => [!type]= blocks to styled HTML =
= elements. 8. *Accept some loss.* Block references, transclusion, the graph view, backlinks, and tag navigation are inherently Obsidian features. There is no lossless conversion for these --- document them and move on. *** Versions Used Report generated April 2026. Pandoc behavior references version 3.x. Obsidian syntax references are based on Obsidian 1.x with its default feature set.