This page was generated from README.md and docs/features.md using Pantsdown.
View source

Pantsdown

Pantsdown is a Markdown to HTML converter. It attempts to render markdown similar to how GitHub does it plus some features developed specifically for github-preview.nvim.

▶️ Demo

📦 Installation

This package is distributed only as a TypeScript module. This means you'll need a bundler to handle transpilation. See below for usage examples.

# bun
bun install pantsdown
# npm
npm install pantsdown

💻 Usage

🚨 Pantsdown does not sanitize the output HTML. If you are processing potentially unsafe strings, it's recommended you use a sanitization library like DOMPurify.

Styles

For styles to be properly applied, either the element containing the generated html or one of its parents must have the classes class="pantsdown light" or class="pantsdown dark" added. You can also add the class "high-contrast" to enable high-contrast themes class="pantsdown dark high-contrast" or class="pantsdown light high-contrast".

Bun

Take a look at how Pantsdown's demo is built for a very simple usage example with Bun.

Vite

Create a Vite Project & install dependencies:

bun create vite my-app --template react-swc-ts
cd my-app
bun install pantsdown

Remove CSS from my-app/src/main.tsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
- import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

Replace content in my-app/src/App.tsx:

import { Pantsdown } from "pantsdown";
import "pantsdown/styles.css";
import { useEffect } from "react";

const pantsdown = new Pantsdown();

function App() {
  useEffect(() => {
    const container = document.getElementById("markdown-container");
    if (!container) return;

    const markdown = "# Hello world\n- [ ] Task 1\n- [x] Task 2";
    const { html, javascript } = pantsdown.parse(markdown);
    container.innerHTML = html;

    const newScript = document.createElement("script");
    newScript.text = javascript;
    container.appendChild(newScript);
  }, []);

  // ⚠️ for styles to be applied, a parent element must have
  // the classes "pantsdown light" or "pantsdown dark" added
  return <div id="markdown-container" className="pantsdown light" />;
}

export default App;

⚙️ Configuration

The Pantsdown constructor accepts an optional configuration object. If you

import { Pantsdown, type PartialPantsdownConfig } from "pantsdown";

// This is the default config object. If you provide
// a config object, it will be deeply merged into this.
const config: PartialPantsdownConfig = {
   renderer: {
      /**
       * Prefix to be added to relative image sources.
       * Must start and end with "/"
       *
       * @example
       * relativeImageUrlPrefix: "/__localimage__/nested/directory/"
       *
       * ![image](./wallpants-512.png)
       * relative src is updated and results in:
       * <img src="/__localimage__/nested/directory/wallpants-512.png" />
       *
       * ![image](https://avatars.githubusercontent.com/wallpants)
       * absolute src remains unchanged:
       * <img src="https://avatars.githubusercontent.com/wallpants" />
       */
      relativeImageUrlPrefix: string;

      /**
       * Prefix to be added to absolute image sources.
       * Must start and end with "/"
       * Falls back to `relativeImageUrlPrefix` if not provided
       *
       * @example
       * absoluteImageUrlPrefix: "/__localimage__/"
       *
       * ![image](/wallpants-512.png)
       * relative src is updated and results in:
       * <img src="/__localimage__/wallpants-512.png" />
       *
       * ![image](https://avatars.githubusercontent.com/wallpants)
       * external src remains unchanged:
       * <img src="https://avatars.githubusercontent.com/wallpants" />
       */
      absoluteImageUrlPrefix?: string | undefined;

      /**
       * Whether to render <details> html tags with attribute `open=""`
       *
       * @default
       * false
       */
      detailsTagDefaultOpen: boolean;

      /**
       * Interactive controls added to rendered mermaid diagrams.
       */
      mermaid: {
         /**
          * Control buttons overlaid on the diagram.
          * Set individual buttons to `false` to hide them.
          *
          * @default
          * { zoom: true, reset: true, arrows: true, popover: true }
          */
         buttons: {
            /** Zoom in & zoom out buttons */
            zoom: boolean;
            /** Reset zoom & pan to the initial position */
            reset: boolean;
            /** Directional pan arrows */
            arrows: boolean;
            /** Open the diagram in a fullscreen popover */
            popover: boolean;
         };

         /**
          * Enable cmd/ctrl + drag to pan and cmd/ctrl + scroll to zoom.
          * Plain click & scroll are never captured, so text selection
          * and page scrolling keep working. Inside the popover, plain
          * drag pans and plain scroll zooms regardless of this setting.
          *
          * @default
          * true
          */
         mouseActions: boolean;
      };
   };
};

const pantsdown = new Pantsdown(config);
const { html, javascript } = pantsdown.parse(markdown);

console.log(html, javascript);

🤝 Acknowledgements

Pantsdown is based on Marked. Without their hard work, Pantsdown would not exist.

Last synced with Marked v18.0.7.

Features

Light and Dark modes


Lists


Images

wallpants


Tables

Syntax Description
Header Title
Paragraph Text

Code Highlight & Copy to Clipboard Button

Supported languages.


Header Slugs

Jump to #images


Alerts

Note

Highlights information that users should take into account, even when skimming.

Tip

Optional information to help a user be more successful.

Important

Crucial information necessary for users to succeed.

Warning

Critical content demanding immediate user attention due to potential risks.

Caution

Negative potential consequences of an action.


<details>

Summary

Some content:

echo "hello world"

Footnotes

Here's a simple footnote,1 and here's a longer one.bignote


LaTeX / Math

Inline math: $E = mc^2$ and $a^2 + b^2 = c^2$

Inline math: and

Block math with $$:

$$
\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
$$

Math code block:

```math
\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}
```
Note

Consumers must include KaTeX CSS for proper rendering:

<link rel="stylesheet" href="katex/dist/katex.min.css" />

Mermaid

Take a look at how Pantsdown's demo is built for an example setup.

Rendered diagrams are interactive: cmd/ctrl + drag pans, cmd/ctrl + scroll zooms (plain click & scroll keep selecting text and scrolling the page), and the overlaid buttons pan, zoom, reset the view or open the diagram in a fullscreen popover, where plain drag pans and plain scroll zooms. Buttons and mouse actions can be toggled via the renderer.mermaid config.

Each diagram's pan/zoom state is keyed by a data-mermaid-key attribute (a hash of its source), so consumers that re-render the whole document on every update — like github-preview.nvim — keep the current view in place; the state resets when the diagram's source changes or the document no longer contains it.

```mermaid
sequenceDiagram
    participant Alice
    participant Bob
    Alice->>John: Hello John, how are you?
    loop Healthcheck
        John->>John: Fight against hypochondria
    end
    Note right of John: Rational thoughts <br/>prevail!
    John-->>Alice: Great!
    John->>Bob: How about you?
    Bob-->>John: Jolly good!
```
sequenceDiagram
    participant Alice
    participant Bob
    Alice->>John: Hello John, how are you?
    loop Healthcheck
        John->>John: Fight against hypochondria
    end
    Note right of John: Rational thoughts <br/>prevail!
    John-->>Alice: Great!
    John->>Bob: How about you?
    Bob-->>John: Jolly good!
  1. This is the first footnote.

  2. Here's one with multiple paragraphs and code. Indent paragraphs to include them in the footnote. { my code } Add as many paragraphs as you like.