notion-markup
v1.1.0
Published
Bidirectional conversion between Notion API blocks, HTML, and Markdown
Downloads
215
Maintainers
Readme
notion-markup
Bidirectional conversion between Notion API blocks, HTML, and Markdown.
- Zero browser dependencies — runs in Node.js and edge runtimes
- No external CSS or JS — HTML output uses only inline styles
- Full coverage of all 34 Notion block types
- Handles any HTML — not just Notion-generated markup
Installation
npm install notion-markupCLI
Use it directly with npx — no install required:
npx notion-markup <operation> [options]Operations
| Operation | Description |
|---|---|
| notion-to-html | Convert Notion blocks JSON to HTML |
| notion-to-markdown | Convert Notion blocks JSON to Markdown |
| html-to-notion | Convert HTML to Notion blocks JSON |
| html-to-markdown | Convert HTML to Markdown |
| markdown-to-html | Convert Markdown to HTML |
| markdown-to-notion | Convert Markdown to Notion blocks JSON |
Input methods
| Flag | Description |
|---|---|
| -i, --input <string> | Pass input as a string argument |
| -f, --file <path> | Read input from a file |
| (stdin) | Pipe input via stdin |
| -h, --help | Show help |
Examples
# Pipe via stdin
echo '<h1>Hello</h1>' | npx notion-markup html-to-markdown
# Inline input
npx notion-markup markdown-to-html -i "# Hello"
# From file
npx notion-markup html-to-notion -f page.html
# Chain conversions
cat blocks.json | npx notion-markup notion-to-html | npx notion-markup html-to-markdownQuick Start
import {
notionToHtml,
htmlToNotionBlocks,
notionToMarkdown,
markdownToNotionBlocks,
htmlToMarkdown,
markdownToHtml,
} from "notion-markup";
// Notion blocks → HTML
const html = notionToHtml(blocks);
// HTML → Notion blocks
const blocks = htmlToNotionBlocks(html);
// Notion blocks → Markdown
const md = await notionToMarkdown(blocks);
// Markdown → Notion blocks
const blocks = await markdownToNotionBlocks(md);
// HTML ↔ Markdown (standalone)
const md = await htmlToMarkdown(html);
const html = await markdownToHtml(md);API Reference
notionToHtml(blocks: Block[]): string
Converts an array of Notion API block objects into an HTML string (body content only, no <html> or <head> wrapper).
Returns inline-styled HTML with no external CSS or JavaScript dependencies.
htmlToNotionBlocks(html: string): Block[]
Parses an HTML string and returns an array of Notion API block objects. Handles all standard HTML5 tags, not just Notion-generated HTML.
notionToMarkdown(blocks: Block[]): Promise<string>
Converts Notion blocks to GFM-compatible Markdown. Internally converts to HTML first, then to Markdown via the unified ecosystem.
markdownToNotionBlocks(markdown: string): Promise<Block[]>
Parses a Markdown string and returns Notion API block objects. Supports GFM extensions (tables, strikethrough, task lists).
htmlToMarkdown(html: string): Promise<string>
Direct HTML → Markdown conversion using rehype-remark.
markdownToHtml(markdown: string): Promise<string>
Direct Markdown → HTML conversion using remark-rehype.
Notion Block → HTML Mapping
| Notion Block Type | HTML Output |
|---|---|
| paragraph | <p> with rich text tags |
| heading_1 | <h1> |
| heading_2 | <h2> |
| heading_3 | <h3> |
| heading_* (toggleable) | <details><summary><h*> |
| bulleted_list_item | <ul><li> (auto-grouped) |
| numbered_list_item | <ol><li> (auto-grouped) |
| to_do | <ul><li class="to-do"><input type="checkbox"> |
| toggle | <details><summary> |
| quote | <blockquote> |
| callout | <div class="callout"> with icon + background color |
| code | <pre><code class="language-*"> |
| equation | <div class="equation" data-expression="..."> |
| divider | <hr/> |
| image | <figure><img> with optional <figcaption> |
| video | <figure><video controls> |
| audio | <audio controls> |
| file | <a href="..." class="file-download"> |
| pdf | <figure><iframe> |
| embed | <figure><iframe> |
| bookmark | <a class="bookmark"> |
| link_preview | <a class="link-preview"> |
| table | <table> with <thead>/<tbody> |
| table_row | <tr> with <th> or <td> |
| column_list | <div class="column-list" style="display:flex"> |
| column | <div class="column" style="width:..."> |
| synced_block | Renders children directly |
| table_of_contents | <nav class="table-of-contents"> |
| breadcrumb | <nav class="breadcrumb"> |
| child_page | <div class="child-page"><a> |
| child_database | <div class="child-database"> |
| template | <div class="template"> |
| link_to_page | <a class="link-to-page"> |
| unsupported | <!-- unsupported block --> |
HTML → Notion Block Mapping
| HTML Element | Notion Block Type |
|---|---|
| <h1> | heading_1 |
| <h2> | heading_2 |
| <h3> | heading_3 |
| <h4>, <h5>, <h6> | heading_3 (downgraded) |
| <p> | paragraph |
| <ul><li> | bulleted_list_item |
| <ol><li> | numbered_list_item |
| <li> with checkbox | to_do |
| <blockquote> | quote |
| <pre><code> | code |
| <hr> | divider |
| <img> | image |
| <video> | video |
| <audio> | audio |
| <iframe> | embed |
| <table> | table + table_row |
| <details><summary> | toggle or toggleable heading |
| <a class="bookmark"> | bookmark |
| <div class="callout"> | callout |
| <div class="equation"> | equation |
| <div class="column-list"> | column_list + column |
| <nav class="table-of-contents"> | table_of_contents |
| <div class="child-page"> | child_page |
| <div class="child-database"> | child_database |
Rich Text Annotations
| Annotation | HTML Render | HTML Parse |
|---|---|---|
| bold | <strong> | <strong>, <b> |
| italic | <em> | <em>, <i>, <cite>, <dfn>, <var> |
| strikethrough | <s> | <s>, <del>, <strike> |
| underline | <u> | <u>, <ins> |
| code | <code> | <code>, <kbd>, <samp> |
| color (text) | <span style="color:..."> | <span style="color:..."> |
| color (background) | <span style="background-color:..."> | <span style="background-color:..."> |
| — | — | <mark> → yellow_background |
Extra HTML Tags Handling
Tags not native to Notion are intelligently mapped:
| HTML Tag | Behavior |
|---|---|
| <section>, <article>, <aside>, <main>, <header>, <footer>, <nav>, <div> | Recurse into children |
| <dl> | <dt> → bold bulleted item, <dd> → indented paragraph |
| <address> | Italic paragraph |
| <picture> | Extracts <img> fallback |
| <figure> | Extracts inner media element |
| <abbr>, <time>, <small>, <sub>, <sup>, <q>, <span> | Pass through as plain text |
| <svg>, <canvas>, <form>, <script>, <style> | Skipped |
Notion Color Support
All 19 Notion colors are supported with bidirectional hex mapping:
| Color | Hex (text) | Background variant |
|---|---|---|
| blue | #337EA9 | blue_background → #D3E5EF |
| brown | #9F6B53 | brown_background → #E9DFDA |
| gray | #787774 | gray_background → #EBECED |
| green | #448361 | green_background → #DBEDDB |
| orange | #D9730D | orange_background → #FADEC9 |
| pink | #C14C8A | pink_background → #F5E0E9 |
| purple | #9065B0 | purple_background → #E8DEEE |
| red | #D44C47 | red_background → #FFE2DD |
| yellow | #CB912F | yellow_background → #FDE4AC |
TypeScript Types
All types are fully exported:
import type {
Block,
RichText,
RichTextText,
RichTextMention,
RichTextEquation,
Annotations,
Color,
FileObject,
Icon,
TextBlockContent,
HeadingContent,
ToDoContent,
CodeContent,
CalloutContent,
EquationContent,
BookmarkContent,
EmbedContent,
TableContent,
TableRowContent,
ColumnListContent,
ColumnContent,
SyncedBlockContent,
LinkToPageContent,
NumberedListItemContent,
TableOfContentsContent,
} from "notion-markup";How It Works
Notion Blocks ──→ notionToHtml() ──→ HTML
│
├──→ htmlToMarkdown() ──→ Markdown
│
└──→ htmlToNotionBlocks() ──→ Notion Blocks
Markdown ──→ markdownToHtml() ──→ HTML ──→ htmlToNotionBlocks() ──→ Notion BlocksComposed pipelines:
notionToMarkdown()=notionToHtml()→htmlToMarkdown()markdownToNotionBlocks()=markdownToHtml()→htmlToNotionBlocks()
The HTML↔Markdown conversions use the unified ecosystem (rehype-remark, remark-rehype, remark-gfm).
License
MIT
