Martian
Markdown to Notion: Convert Markdown and GitHub Flavoured Markdown to Notion API Blocks and RichText đđ
Install / Use
/learn @tryfabric/MartianREADME
Martian: Markdown to Notion Parser
Convert Markdown and GitHub Flavoured Markdown to Notion API Blocks and RichText.
Martian is a Markdown parser to convert any Markdown content to Notion API block or RichText objects. It uses unified to create a Markdown AST, then converts the AST into Notion objects.
Designed to make using the Notion SDK and API easier. Notion API version 1.0.
Supported Markdown Elements
- All inline elements (italics, bold, strikethrough, inline code, hyperlinks, equations)
- Lists (ordered, unordered, checkboxes) - to any level of depth
- All headers (header levels >= 3 are treated as header level 3)
- Code blocks, with language highlighting support
- Block quotes
- Supports GFM alerts (e.g. [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION])
- Supports Notion callouts when blockquote starts with an emoji (optional, enabled with
enableEmojiCallouts) - Automatically maps common emojis and alert types to appropriate background colors
- Preserves formatting and nested blocks within callouts
- Tables
- Equations
- Images
- Inline images are extracted from the paragraph and added afterwards (as these are not supported in notion)
- Image urls are validated, if they are not valid as per the Notion external spec, they will be inserted as text for you to fix manually
Usage
Basic usage:
The package exports two functions, which you can import like this:
// JS
const {markdownToBlocks, markdownToRichText} = require('@tryfabric/martian');
// TS
import {markdownToBlocks, markdownToRichText} from '@tryfabric/martian';
Here are couple of examples with both of them:
markdownToRichText(`**Hello _world_**`);
<details>
<summary>Result</summary>
<pre>
[
{
"type": "text",
"annotations": {
"bold": true,
"strikethrough": false,
"underline": false,
"italic": false,
"code": false,
"color": "default"
},
"text": {
"content": "Hello "
}
},
{
"type": "text",
"annotations": {
"bold": true,
"strikethrough": false,
"underline": false,
"italic": true,
"code": false,
"color": "default"
},
"text": {
"content": "world"
}
}
]
</pre>
</details>
markdownToBlocks(`
hello _world_
***
## heading2
* [x] todo
> đ **Note:** Important _information_
> Some other blockquote
`);
<details>
<summary>Result</summary>
<pre>
[
{
"object": "block",
"type": "paragraph",
"paragraph": {
"rich_text": [
{
"type": "text",
"annotations": {
"bold": false,
"strikethrough": false,
"underline": false,
"italic": false,
"code": false,
"color": "default"
},
"text": {
"content": "hello "
}
},
{
"type": "text",
"annotations": {
"bold": false,
"strikethrough": false,
"underline": false,
"italic": true,
"code": false,
"color": "default"
},
"text": {
"content": "world"
}
}
]
}
},
{
"object": "block",
"type": "divider",
"divider": {}
},
{
"object": "block",
"type": "heading_2",
"heading_2": {
"rich_text": [
{
"type": "text",
"annotations": {
"bold": false,
"strikethrough": false,
"underline": false,
"italic": false,
"code": false,
"color": "default"
},
"text": {
"content": "heading2"
}
}
]
}
},
{
"object": "block",
"type": "to_do",
"to_do": {
"rich_text": [
{
"type": "text",
"annotations": {
"bold": false,
"strikethrough": false,
"underline": false,
"italic": false,
"code": false,
"color": "default"
},
"text": {
"content": "todo"
}
}
],
"checked": true
}
},
{
"type": "callout",
"callout": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Note:"
},
"annotations": {
"bold": true,
"strikethrough": false,
"underline": false,
"italic": false,
"code": false,
"color": "default"
}
},
{
"type": "text",
"text": {
"content": " Important "
}
},
{
"type": "text",
"text": {
"content": "information"
},
"annotations": {
"bold": false,
"strikethrough": false,
"underline": false,
"italic": true,
"code": false,
"color": "default"
}
}
],
"icon": {
"type": "emoji",
"emoji": "đ"
},
"color": "blue_background"
}
},
{
"type": "quote",
"quote": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Some other blockquote"
},
"annotations": {
"bold": false,
"strikethrough": false,
"underline": false,
"italic": false,
"code": false,
"color": "default"
}
}
]
}
}
]
</pre>
</details>
Working with blockquotes
Martian supports three types of blockquotes:
- Standard blockquotes:
> This is a regular blockquote
> It can span multiple lines
- GFM alerts (based on GFM Alerts):
> [!NOTE]
> Important information that users should know
> [!WARNING]
> Critical information that needs attention
- Emoji-style callouts (optional) (based on ReadMe's markdown callouts):
> đ **Note:** This is a callout with a blue background
> It supports all markdown formatting and can span multiple lines
> â **Warning:** This is a callout with a red background
> Perfect for important warnings
GFM Alerts
GFM alerts are automatically converted to Notion callouts with appropriate icons and colors:
- NOTE (đ, blue): Useful information that users should know
- TIP (đĄ, green): Helpful advice for doing things better
- IMPORTANT (âī¸, purple): Key information users need to know
- WARNING (â ī¸, yellow): Urgent info that needs immediate attention
- CAUTION (â, red): Advises about risks or negative outcomes
Emoji-style Callouts
By default, emoji-style callouts are disabled. You can enable them using the enableEmojiCallouts option:
const options = {
enableEmojiCallouts: true,
};
When enabled, callouts are detected when a blockquote starts with an emoji. The emoji determines the callout's background color. The current supported color mappings are:
- đ (blue): Perfect for notes and information
- đ (green): Success messages and tips
- â (red): Warnings and important notices
- đ§ (yellow): Work in progress or caution notices
All other emojis will have a default background color. The supported emoji color mappings can be expanded easily if needed.
If a blockquote doesn't match either GFM alert syntax or emoji-style callout syntax (when enabled), it will be rendered as a Notion quote block.
Examples
Standard blockquote:
markdownToBlocks('> A regular blockquote');
<details>
<summary>Result</summary>
<pre>
[
{
"object": "block",
"type": "quote",
"quote": {
"rich_text": [
{
"type": "text",
"text": {
"content": "A regular blockquote"
}
}
]
}
}
]
</pre>
</details>
GFM alert:
markdownToBlocks('> [!NOTE]\n> Important information');
<details>
<summary>Result</summary>
<pre>
[
{
"object": "block",
"type": "callout",
"callout": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Note"
}
}
],
"icon": {
"type": "emoji",
"emoji": "âšī¸"
},
"color": "blue_background",
"children": [
{
"type": "paragraph",
"paragraph": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Important information"
}
}
]
}
}
]
}
}
]
</pre>
</details>
Emoji-style callout (with enableEmojiCallouts: true):
markdownToBlocks('> đ Note: Important information', {
enableEmojiCallouts: true,
});
<details>
<summary>Result</summary>
<pre>
[
{
"object": "block",
"type": "callout",
"callout": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Note: Important information"
}
}
],
"icon": {
"type": "emoji",
"emoji": "đ"
},
"color": "blue_background"
}
}
]
</pre>
</details>
Working with Notion's limits
Sometimes a Markdown input would result in an output that would be rejected by the Notion API: here are some options to deal with that.
An item exceeds the children or character limit
By default, the package will try to resol
