remark-admonition-to-blockquote-callout
v1.0.0
Published
Remark plugin for converting Python-Markdown and MkDocs Material admonitions to blockquote callouts.
Maintainers
Readme
remark-admonition-to-blockquote-callout
A remark plugin for converting Python-Markdown admonitions and MkDocs Material admonitions to blockquote callouts.
What is this?
This plugin lets source files keep Python-Markdown and MkDocs Material admonition syntax while converting it to blockquote callout syntax:
- Basic and titled admonitions:
!!! note->> [!note],!!! note "Title"->> [!note] Title. - Markdown titles are preserved:
!!! note "A **bold** title"->> [!note] A **bold** title. - Explicit empty titles are treated the same as omitted titles:
!!! note ""->> [!note]. - Collapsible blocks:
??? note->> [!note]-,???+ note->> [!note]+. - Nested admonitions become nested blockquotes:
!!! note->> > [!note]. - Extra classes are ignored after the type:
!!! danger highlight blink "Title"->> [!danger] Title. - Inline modifiers are ignored:
!!! info inline "Title"->> [!info] Title,!!! info inline end "Title"->> [!info] Title. - MkDocs Material and custom types are kept as callout types.
- Fenced code blocks inside admonitions are preserved.
- Admonitions inside list items keep their list-item indentation.
When should I use this?
Use this plugin when your Markdown content is written with Python-Markdown or MkDocs Material admonition syntax, but the rest of your unified pipeline expects blockquote-based callouts.
This is especially useful when migrating documentation to a Markdown/MDX site powered by frameworks such as Astro or Next.js: keep Python-Markdown admonition syntax in source files, convert it in remark, and consider rehype-callouts if you also need to render the generated blockquote-based callouts as HTML.
Installation
This package is ESM only. In Node.js (version 16+), install with your package manager:
npm install remark-admonition-to-blockquote-callout
yarn add remark-admonition-to-blockquote-callout
pnpm add remark-admonition-to-blockquote-calloutIn Deno with esm.sh:
import remarkAdmonitionToBlockquoteCallout from 'https://esm.sh/remark-admonition-to-blockquote-callout'In browsers with esm.sh:
<script type="module">
import remarkAdmonitionToBlockquoteCallout from 'https://esm.sh/remark-admonition-to-blockquote-callout?bundle'
</script>Usage
Import and use the plugin before other remark plugins whose mdast changes must be preserved.
Say example.md contains:
!!! note
Basic content.
!!! info "Custom **title**"
Markdown title content.
???+ tip inline "Open"
Open by default.
```js
const marker = "!!! note";
```
!!! success ""
Nested empty-title admonition.
??? failure inline end "Closed"
Closed by default.
* In a list item:
!!! custom highlight
Extra classes are ignored.And module example.js contains:
import { remark } from 'remark'
import remarkAdmonitionToBlockquoteCallout from 'remark-admonition-to-blockquote-callout'
import { read } from 'to-vfile'
const file = await remark()
.use(remarkAdmonitionToBlockquoteCallout)
.process(await read('example.md'))
// `remark` escapes callout markers as `\[!type]` for safe Markdown output,
// preventing `[!type]` from being parsed as a link reference. Restore only
// blockquote callouts for Markdown text; AST-based unified transforms do not need it.
const output = String(file).replaceAll(
/^(\s*>(?: >)*\s*)\\(\[![^\]\n]+])/gm,
'$1$2'
)
console.log(output)Then running node example.js yields:
> [!note]
> Basic content.
> [!info] Custom **title**
> Markdown title content.
> [!tip]+ Open
> Open by default.
>
> ```js
> const marker = "!!! note";
> ```
>
> > [!success]
> > Nested empty-title admonition.
> [!failure]- Closed
> Closed by default.
* In a list item:
> [!custom]
> Extra classes are ignored.[!warning] When this plugin converts admonitions, it reparses the transformed Markdown and replaces the current mdast root. Place it before remark plugins that mutate mdast if those mutations must be preserved.
When writing admonitions, avoid mixing spaces and tabs for indentation. Mixed indentation can make admonition boundaries ambiguous and may prevent correct conversion.
[!note] This plugin is intentionally lenient when collecting admonition bodies. After an opening marker is found, following lines with deeper indentation are treated as body content, even if they do not follow the indentation rules required by Python-Markdown.
For example, both of these inputs are converted:
!!! note This is a note. !!! note This is a note.> [!note] > This is a note. > [!note] > This is a note.
API
This package exports no identifiers. The default export is remarkAdmonitionToBlockquoteCallout.
unified().use(remarkAdmonitionToBlockquoteCallout)
Used to convert Python-Markdown and MkDocs Material admonitions to blockquote callouts.
Parameters
There are no options.
Returns
Transform (Transformer).
Example
Example: Render Python-Markdown Admonitions as HTML
This plugin only converts admonition Markdown syntax to blockquote callouts; use rehype-callouts later in the pipeline when those callouts should be rendered as HTML.
Module example.js contains:
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkAdmonitionToBlockquoteCallout from 'remark-admonition-to-blockquote-callout'
import remarkRehype from 'remark-rehype'
import rehypeCalloouts from 'rehype-callouts'
import rehypeStringify from 'rehype-stringify'
import { readSync } from 'to-vfile'
const processor = unified()
.use(remarkParse)
.use(remarkAdmonitionToBlockquoteCallout)
.use(remarkRehype)
.use(rehypeCalloouts, {
callouts: {
custom: { title: 'Custom Type' },
},
})
.use(rehypeStringify)
const output = String(
processor.processSync(readSync('example.md'))
)
console.log(output)Then running node example.js yields:
<div class="callout" data-callout="note" data-collapsible="false">
<div class="callout-title">
<div class="callout-title-icon" aria-hidden="true">
<!-- svg icon-->
</div>
<div class="callout-title-text">Note</div>
</div>
<div class="callout-content"><p>Basic content.</p></div>
</div>
<div class="callout" data-callout="info" data-collapsible="false">
<div class="callout-title">
<div class="callout-title-icon" aria-hidden="true">
<!-- svg icon-->
</div>
<div class="callout-title-text">Custom <strong>title</strong></div>
</div>
<div class="callout-content"><p>Markdown title content.</p></div>
</div>
<details class="callout" data-callout="tip" data-collapsible="true" open>
<summary class="callout-title">
<div class="callout-title-icon" aria-hidden="true">
<!-- svg icon-->
</div>
<div class="callout-title-text">Open</div>
<div class="callout-fold-icon" aria-hidden="true">
<!-- svg icon-->,
</div>
</summary>
<div class="callout-content">
<p>Open by default.</p>
<pre><code class="language-js">const marker = "!!! note";</code></pre>
<div class="callout" data-callout="success" data-collapsible="false">
<div class="callout-title">
<div class="callout-title-icon" aria-hidden="true">
<!-- svg icon-->
</div>
<div class="callout-title-text">Success</div>
</div>
<div class="callout-content"><p>Nested empty-title admonition.</p></div>
</div>
</div>
</details>
<details class="callout" data-callout="failure" data-collapsible="true">
<summary class="callout-title">
<div class="callout-title-icon" aria-hidden="true">
<!-- svg icon-->
</div>
<div class="callout-title-text">Closed</div>
<div class="callout-fold-icon" aria-hidden="true">
<!-- svg icon-->
</div>
</summary>
<div class="callout-content"><p>Closed by default.</p></div>
</details>
<ul>
<li>
<p>In a list item:</p>
<div class="callout" data-callout="custom" data-collapsible="false">
<div class="callout-title">
<div class="callout-title-text">Custom Type</div>
</div>
<div class="callout-content"><p>Extra classes are ignored.</p></div>
</div>
</li>
</ul>Types
This package is fully typed with TypeScript. It exports no additional public types.
Contribution
If you see any errors or room for improvement on this plugin, feel free to open an issues or pull request . Thank you in advance for contributing!
License
MIT © 2026-PRESENT Stephanie Lin
