# How to get the rendered markdown html?

**URL:** <https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020>\
**Category:** Devs: Plugins & API\
**Created:** [November 5, 2024, 3:49am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020 "2024-11-05T03:49:16Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 5, 2024, 3:49am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/1 "2024-11-05T03:49:16Z")

</div>

so i’m new to obsidian’s plugin api. i have been trying to make a simple plugin to get the rendered html version of the markdown out of the view and save it to the clipboard. i have a few really cool plugins and i’d love to quickly copy paste my notes around the web.

my issue is the `MarkdownView` passed to the editor callback only seems to have the visible portion of the html in: `view.contentEl.innerHTML`. i played around trying to force a full render by calling `view.previewMode.rerender(true);` before line, w/o any luck. am i looking at the wrong object? here’s a snip of the plugin:

```auto
this.addCommand({
	id: "html-copy",
	name: "copy renders file html",
	editorCallback: async (editor: Editor, view: MarkdownView) => {
		let dom = view.contentEl.innerHTML; // only gets part
		navigator.clipboard.writeText(dom);
		new Notice(html saved to the clipboard, 3500);
	},
});

```

i also tried the `get` method on the `previewMode` object, but that surprisingly returned the markdown itself.

```auto
view.previewMode.rerender(true);
const dom = view.previewMode.get();
navigator.clipboard.writeText(dom);
new Notice(html saved to the clipboard, 3500);

```

i appreciate any suggestions or incites

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 5, 2024, 6:24am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/2 "2024-11-05T06:24:53Z")

</div>

i tried using find selecting the root `.mardown-reading-view` and i get the same clipping issue. i noticed if i scroll whatever is in view is what’s returned.

`navigator.clipboard.writeText(view.contentEl.find(".markdown-reading-view").innerHTML);`

---

<div class="post-metadata">

**Author:** ![Tu2\_atmanand](https://forum.obsidian.md/user_avatar/forum.obsidian.md/tu2_atmanand/32/71676_2.png) [@Tu2\_atmanand](https://forum.obsidian.md/u/Tu2_atmanand)\
**Post date:** [November 5, 2024, 6:39am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/3 "2024-11-05T06:39:29Z")

</div>

Thats a good Idea for a plugin !

Although I havent tried to read what is returned by `MarkdownRenderer.render()` API, but I have used it a lot to render HTML of the part of Raw text I wanted to render inside any Modal or View. Since I am appending it inside the HTML DIV, so i believe it is returning HTML code. But You can try it if that works.

The only issue will be you will need to pass the parent MD file and the content yourself to the API. (Well, this could be even a feature, like the user can select a part of the content from the view, and then right click to get the HTML for the same and you can get that specific part as HTML inside the clipboard)

Check-out this post, on how to use it : [I want to support some math notations,replace $$,$$ to \[, \] - #2 by Tu2\_atmanand](https://forum.obsidian.md/t/i-want-to-support-some-math-notations-replace-to/90602/2)

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 5, 2024, 12:21pm UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/4 "2024-11-05T12:21:46Z")

</div>

appreciate it!

i was reading this actually last night, but i couldn’t figure out what component i needed to pass. your example of creating a blank one is very helpful. i’ll give it a go, thanx!

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 5, 2024, 4:31pm UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/5 "2024-11-05T16:31:14Z")

</div>

i got it! thanx for your help @Tu2_atmanand !

for future readers:

```auto
import {
	App,
	Editor,
	Notice,
	Plugin,
	PluginSettingTab,
	MarkdownView,
	Component,
	MarkdownRenderer,
} from "obsidian";

export default class md2html extends Plugin {
	async getHtml(md: string, file: string, view: MarkdownView) {
		const component = new Component();
		component.load();
		const renderDiv = view.contentEl;
		await MarkdownRenderer.render(this.app, md, renderDiv, file, component);
		return renderDiv.innerHTML
			.split('markdown-reading-view')[1]
			.split('</div></div></div>')[1];
	}
	async onload() {
		this.addCommand({
			id: "md2html-new",
			name: "convert to new file",
			editorCallback: async (_editor: Editor, view: MarkdownView) => {
				const file = this.app.workspace.getActiveFile()?.name || "nop";
				const dom = await this.getHtml(view.getViewData(), file, view);
				this.app.vault.create("html-" + file, dom);
				new Notice("document converted to new html file", 3500);
			},
		});
		this.addCommand({
			id: "md2html-clip",
			name: "convert to clipboard",
			editorCallback: async (_editor: Editor, view: MarkdownView) => {
				const file = this.app.workspace.getActiveFile()?.name || "nop";
				navigator.clipboard.writeText(
					await this.getHtml(view.getViewData(), file, view),
				);
				new Notice("document html saved to the clipboard", 3500);
			},
		});
	}
};

```

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 6, 2024, 6:30am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/6 "2024-11-06T06:30:34Z")

</div>

i’m still working on refining the results. you can follow my work or checkout the plugin here: [GitHub - xero/obsidian-md2html: plugin to convert markdown to html inside obsidian.md](https://github.com/xero/obsidian-md2html)

---

<div class="post-metadata">

**Author:** ![mnaoumov](https://forum.obsidian.md/user_avatar/forum.obsidian.md/mnaoumov/32/22325_2.png) [@mnaoumov](https://forum.obsidian.md/u/mnaoumov)\
**Post date:** [November 6, 2024, 1:58pm UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/7 "2024-11-06T13:58:58Z")

</div>

I think your implementation can be slightly simplified

> <https://github.com/mnaoumov/obsidian-dev-utils/blob/main/src/obsidian/Markdown.ts#L20>

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 7, 2024, 10:56pm UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/8 "2024-11-07T22:56:24Z")

</div>

@mnaoumov that’s a great solution and works if you want to just render some markdown. but i want to get all the rendered results of my plugins as well. things like shiki, expressive code, dataview tablets, etc.

i’ve done a bunch more testing, trying a new approach, this code _ALMOST_ works:

```auto
...
	async onload() {
		this.addCommand({
			id: "md2html-clip",
			name: "note html to clipboard",
			callback: () => {
				const html = this.app.workspace.getActiveViewOfType(MarkdownView)?.contentEl.innerHTML;

				if (html !== "") {
					navigator.clipboard.writeText(html);
					new Notice("html copied to the clipboard", 3456);
				}
			},
		});
....

```

the problem is it only returns what’s viewable on the screen at the time.  
i tired adding:

```auto
editor.scrollTo(0, max);
view.previewMode.applyScroll(max);

```

with no luck.

**is there a way to force the reading mode to render everything?**

e.g. i tried poking around the api, but couldn’t find a way to force resize the viewport then rerender

---

<div class="post-metadata">

**Author:** ![mnaoumov](https://forum.obsidian.md/user_avatar/forum.obsidian.md/mnaoumov/32/22325_2.png) [@mnaoumov](https://forum.obsidian.md/u/mnaoumov)\
**Post date:** [November 8, 2024, 12:15am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/9 "2024-11-08T00:15:51Z")

</div>

@xer0 That’s an interesting problem

Try the following

> <https://github.com/mnaoumov/obsidian-dev-utils/blob/644356a1e423d10904d3af9e8aef867c8d94d005/src/obsidian/MarkdownView.ts#L14>

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 8, 2024, 5:38am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/10 "2024-11-08T05:38:07Z")

</div>

thanx so much @mnaoumov

i had to add a ts-ignore statement [[cite](https://github.com/obsidianmd/obsidian-developer-docs/blob/54ba4502b1cb1ac860b04c14f781df43648a1a13/en/Plugins/Editor/Communicating%20with%20editor%20extensions.md?plain=1#L8)] to get it to build:

```auto
// @ts-expect-error, not typed
view.editor.cm

```

but this worked like a charm! now i just need to work out how to force scroll the view to the bottom.

---

<div class="post-metadata">

**Author:** ![mnaoumov](https://forum.obsidian.md/user_avatar/forum.obsidian.md/mnaoumov/32/22325_2.png) [@mnaoumov](https://forum.obsidian.md/u/mnaoumov)\
**Post date:** [November 8, 2024, 5:50am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/11 "2024-11-08T05:50:31Z")

</div>

If you use `npm install obsidian-typings@beta --save-dev` and register the typings according to their README, you won’t need `@ts-expect-error`

---

<div class="post-metadata">

**Author:** ![mnaoumov](https://forum.obsidian.md/user_avatar/forum.obsidian.md/mnaoumov/32/22325_2.png) [@mnaoumov](https://forum.obsidian.md/u/mnaoumov)\
**Post date:** [November 8, 2024, 11:11pm UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/12 "2024-11-08T23:11:17Z")

</div>

> [@xer0](#):
>
> i just need to work out how to force scroll the view to the bottom.

did you try

```js
view.editor.scrollTo(0, Number.MAX_SAFE_INTEGER);

```

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 9, 2024, 8:37pm UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/13 "2024-11-09T20:37:03Z")

</div>

@mnaoumov i did indeed (i did max-1) and it didn’t seem to help.

what did help though, was adding a `sleep(x)` in between the 1st `measure()` and getting the `innerHTML`.

> <https://github.com/xero/obsidian-md2html/blob/main/main.ts#L31>

it still seems to work better if you manually scroll around the document and get it all rended 1st. trying to force it hasn’t gottem me anywhere. but that’s ok. doing it this way still works.

thanx again for all your help on this. i gave you a shoutout on github 😃

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 9, 2024, 8:49pm UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/14 "2024-11-09T20:49:56Z")

</div>

this was my forced scrolling test:

```auto
const cm = view.editor.cm;
cm.focus();
cm.viewState.printing = true;
cm.measure();
sleep(1000000);
view.editor.scrollTo(0, 0);
sleep(1000000);
view.editor.scrollTo(0, Number.MAX_SAFE_INTEGER);
sleep(1000000);
html = view?.contentEl.innerHTML;
cm.viewState.printing = false;
cm.measure();

```

while this get’s the full document (in live preview mode), only the originally visible parts are rendered. the rest is markdown still. in reading mode it doesn’t scroll, so it has no effect. so i left this out of the last release.

---

<div class="post-metadata">

**Author:** ![mnaoumov](https://forum.obsidian.md/user_avatar/forum.obsidian.md/mnaoumov/32/22325_2.png) [@mnaoumov](https://forum.obsidian.md/u/mnaoumov)\
**Post date:** [November 10, 2024, 4:27am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/15 "2024-11-10T04:27:11Z")

</div>

Sleep without `await` doesn’t do anything. I don’t think you meant to wait 1000000 Ms ~ 20 min

---

<div class="post-metadata">

**Author:** ![xer0](https://forum.obsidian.md/user_avatar/forum.obsidian.md/xer0/32/72852_2.png) [@xer0](https://forum.obsidian.md/u/xer0)\
**Post date:** [November 10, 2024, 4:49am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/16 "2024-11-10T04:49:43Z")

</div>

weird that my LSP didn’t tell me that sleep was async. thanks for that again my friend. I was trying increasingly larger values, since like you said, it wasn’t doing anything.

---

<div class="post-metadata">

**Author:** ![mnaoumov](https://forum.obsidian.md/user_avatar/forum.obsidian.md/mnaoumov/32/22325_2.png) [@mnaoumov](https://forum.obsidian.md/u/mnaoumov)\
**Post date:** [November 10, 2024, 4:57am UTC](https://forum.obsidian.md/t/how-to-get-the-rendered-markdown-html/91020/17 "2024-11-10T04:57:12Z")

</div>

I use linter for that, so it doesn’t allow me to have async functions without awaiting.

> **[no-floating-promises | typescript-eslint](https://typescript-eslint.io/rules/no-floating-promises/)**
>
> Require Promise-like statements to be handled appropriately.
