Skip to main content

Overview

The MarkdownRenderer class provides functionality for rendering Markdown content to HTML elements. It extends MarkdownRenderChild and implements MarkdownPreviewEvents and HoverParent interfaces.
Available since version 0.9.7

Properties

app

Reference to the app instance.
app
App
The App instance

hoverPopover

The hover popover associated with this renderer, if any.
hoverPopover
HoverPopover | null
The hover popover or null

file

The file being rendered (abstract getter).
file
TFile
The file being rendered

Static Methods

render()

Renders Markdown string to an HTML element.
app
App
required
A reference to the app object
markdown
string
required
The Markdown source code to render
el
HTMLElement
required
The element to append the rendered content to
sourcePath
string
required
The normalized path of the Markdown file, used to resolve relative internal links
component
Component
required
A parent component to manage the lifecycle of the rendered child components
returns
Promise<void>
A promise that resolves when rendering is complete
// Basic usage
const container = document.createElement('div');
await MarkdownRenderer.render(
  this.app,
  '# Hello World\n\nThis is **bold** text.',
  container,
  '',
  this
);

// Add to DOM
containerEl.appendChild(container);

renderMarkdown() (Deprecated)

Renders Markdown string to an HTML element.
This method is deprecated. Use MarkdownRenderer.render() instead.
markdown
string
required
The Markdown source code
el
HTMLElement
required
The element to append to
sourcePath
string
required
The normalized path of the Markdown file
component
Component
required
A parent component to manage lifecycle
Available since version 0.10.6
// Deprecated - use render() instead
await MarkdownRenderer.renderMarkdown(
  '# Title',
  container,
  '',
  this
);

Examples

Rendering Markdown in a Modal

import { Modal, MarkdownRenderer } from 'obsidian';

class MarkdownModal extends Modal {
  markdown: string;

  constructor(app: App, markdown: string) {
    super(app);
    this.markdown = markdown;
  }

  onOpen() {
    const { contentEl } = this;
    
    // Render the markdown
    MarkdownRenderer.render(
      this.app,
      this.markdown,
      contentEl,
      '',
      this
    );
  }

  onClose() {
    const { contentEl } = this;
    contentEl.empty();
  }
}

// Usage
const modal = new MarkdownModal(
  this.app,
  '# Preview\n\nThis is rendered **Markdown**.'
);
modal.open();
const activeFile = this.app.workspace.getActiveFile();
if (activeFile) {
  const container = containerEl.createDiv();
  
  const markdown = `
See [[Other Note]] for more details.

![Image](assets/image.png)
  `.trim();
  
  // Links will be resolved relative to the active file's path
  await MarkdownRenderer.render(
    this.app,
    markdown,
    container,
    activeFile.path,
    this
  );
}

Rendering File Content

const file = this.app.vault.getAbstractFileByPath('Notes/Example.md');

if (file instanceof TFile) {
  const content = await this.app.vault.read(file);
  const container = containerEl.createDiv();
  
  await MarkdownRenderer.render(
    this.app,
    content,
    container,
    file.path,
    this
  );
}

Rendering in a Custom View

import { ItemView, MarkdownRenderer } from 'obsidian';

class CustomView extends ItemView {
  getViewType(): string {
    return 'custom-view';
  }

  getDisplayText(): string {
    return 'Custom View';
  }

  async onOpen() {
    const container = this.containerEl.children[1];
    container.empty();
    
    const markdown = `
# Custom View Content

This view renders **dynamic** Markdown content.

- Item 1
- Item 2
- Item 3
    `.trim();
    
    await MarkdownRenderer.render(
      this.app,
      markdown,
      container,
      '',
      this
    );
  }
}

Rendering with Component Lifecycle Management

import { Component, MarkdownRenderer } from 'obsidian';

class MarkdownComponent extends Component {
  containerEl: HTMLElement;
  markdown: string;

  constructor(containerEl: HTMLElement, markdown: string) {
    super();
    this.containerEl = containerEl;
    this.markdown = markdown;
  }

  async onload() {
    // Render on load
    await MarkdownRenderer.render(
      this.app,
      this.markdown,
      this.containerEl,
      '',
      this
    );
  }

  onunload() {
    // Clean up on unload
    this.containerEl.empty();
  }
}

// Usage
const component = new MarkdownComponent(
  containerEl,
  '# Dynamic Content'
);
component.load();

// Later, when done:
component.unload();

Rendering Markdown from Frontmatter

const file = this.app.workspace.getActiveFile();

if (file) {
  const cache = this.app.metadataCache.getFileCache(file);
  const description = cache?.frontmatter?.description;
  
  if (description && typeof description === 'string') {
    const container = containerEl.createDiv();
    
    await MarkdownRenderer.render(
      this.app,
      description,
      container,
      file.path,
      this
    );
  }
}

Rendering with Custom Styling

const container = containerEl.createDiv({
  cls: 'custom-markdown-container'
});

const markdown = `
# Styled Content

This content will be rendered with custom styling.
`;

await MarkdownRenderer.render(
  this.app,
  markdown,
  container,
  '',
  this
);

// Add custom CSS
container.style.padding = '1em';
container.style.border = '1px solid var(--background-modifier-border)';
container.style.borderRadius = '4px';

Rendering Multiple Sections

const sections = [
  '# Section 1\n\nFirst section content.',
  '# Section 2\n\nSecond section content.',
  '# Section 3\n\nThird section content.'
];

for (const section of sections) {
  const sectionContainer = containerEl.createDiv({
    cls: 'markdown-section'
  });
  
  await MarkdownRenderer.render(
    this.app,
    section,
    sectionContainer,
    '',
    this
  );
}

Dynamic Content Updates

class DynamicMarkdownRenderer extends Component {
  containerEl: HTMLElement;
  currentContent: string;

  constructor(containerEl: HTMLElement) {
    super();
    this.containerEl = containerEl;
    this.currentContent = '';
  }

  async updateContent(markdown: string) {
    // Clear previous content
    this.containerEl.empty();
    
    // Render new content
    await MarkdownRenderer.render(
      this.app,
      markdown,
      this.containerEl,
      '',
      this
    );
    
    this.currentContent = markdown;
  }
}

// Usage
const renderer = new DynamicMarkdownRenderer(containerEl);
await renderer.updateContent('# Initial Content');

// Later, update with new content
await renderer.updateContent('# Updated Content\n\nNew text here.');

MarkdownRenderChild

Base class that MarkdownRenderer extends.
class MarkdownRenderChild extends Component {
  containerEl: HTMLElement;
  
  constructor(containerEl: HTMLElement);
}

Tips

Always provide a Component instance to manage the lifecycle of rendered content. This ensures proper cleanup when the content is no longer needed.
Use the sourcePath parameter to ensure internal links and embedded images resolve correctly relative to the source file.
The render method is asynchronous. Always await it to ensure the content is fully rendered before interacting with the DOM.

Build docs developers (and LLMs) love