Markdown Exporter to Notion. Converts Markdown files into Notion database pages with hash-based change detection, frontmatter support, and local image uploads.
- Hash-based diff export — Only changed files are re-exported (SHA-256 comparison). Use
--forceto re-export all. - YAML frontmatter — Set page title, tags (multi_select), and category (select) from Markdown frontmatter.
- Local image upload — PNG, JPEG, GIF, and WebP images are automatically uploaded via the Notion File Upload API. External URLs are referenced directly.
- Domain scoping — Share one Notion database across multiple projects using
--domain. - Auto schema setup — Required database properties are created automatically on first run.
- Dry-run mode — Preview the export plan without making any API calls.
go install github.com/m-mizutani/mdex@latest- Go to My Integrations and create a new integration
- Copy the Internal Integration Secret (starts with
ntn_) - Open your target Notion database, click
...> Connections > add your integration
The database ID is part of the Notion database URL. Open the database as a full page in the browser:
https://www.notion.so/{workspace}/{database_id}?v={view_id}
^^^^^^^^^^^^
For example, if the URL is https://www.notion.so/myworkspace/a8b1c2d3e4f5678901234567890abcde?v=..., the database ID is a8b1c2d3e4f5678901234567890abcde.
You can also find it by clicking Copy link to view in the database menu and extracting the ID from the URL.
export MDEX_NOTION_TOKEN="ntn_..."
export MDEX_NOTION_DATABASE_ID="a8b1c2d3e4f5678901234567890abcde"
mdex export --dir ./docsmdex export [flags]
| Flag | Alias | Env Var | Description |
|---|---|---|---|
--notion-database-id |
-d |
MDEX_NOTION_DATABASE_ID |
Notion Database ID |
--notion-token |
-t |
MDEX_NOTION_TOKEN |
Notion Integration Token |
--dir |
MDEX_DIR |
Directory containing Markdown files |
| Flag | Env Var | Default | Description |
|---|---|---|---|
--path-property |
MDEX_PATH_PROPERTY |
mdex_path |
Property name for file path (rich_text) |
--hash-property |
MDEX_HASH_PROPERTY |
mdex_hash |
Property name for content hash (rich_text) |
--tags-property |
MDEX_TAGS_PROPERTY |
Tags |
Property name for tags (multi_select) |
--category-property |
MDEX_CATEGORY_PROPERTY |
Category |
Property name for category (select) |
--domain |
MDEX_DOMAIN |
Domain value for scoping pages | |
--domain-property |
MDEX_DOMAIN_PROPERTY |
Domain |
Property name for domain (select) |
--force |
MDEX_FORCE |
false |
Re-export all files regardless of hash |
--dry-run |
MDEX_DRY_RUN |
false |
Preview export plan without API calls |
--image-base-dir |
MDEX_IMAGE_BASE_DIR |
Base directory for resolving absolute image paths |
Use YAML frontmatter to set Notion page properties:
---
title: Getting Started
tags:
- guide
- setup
category: documentation
---
# Your content here- title — Page title. Falls back to the filename (without
.md) if not specified. - tags — Mapped to a multi_select property.
- category — Mapped to a select property.
If tags or category are omitted, those properties are not set on the page.
The --domain flag allows multiple projects to share a single Notion database without interfering with each other:
# Project A
mdex export --dir ./project-a/docs --domain project-a
# Project B
mdex export --dir ./project-b/docs --domain project-bEach project only sees and manages its own pages. Delete detection is scoped to the domain — removing a file in project A won't affect project B's pages.
If --domain is not specified, no domain property is created and all pages in the database are considered.
- Headings (H1–H3; H4+ mapped to H3)
- Paragraphs
- Fenced code blocks (with language syntax highlighting)
- Blockquotes
- Bulleted lists (with nesting)
- Numbered lists (with nesting)
- Task lists (
- [ ]/- [x]) - Tables (GFM)
- Horizontal rules
- Images (local and external)
- Bold, italic,
strikethrough inline code- Links
| Type | Behavior |
|---|---|
External URL (https://...) |
Referenced directly as an external image block |
| Local file (PNG, JPEG, GIF, WebP) | Uploaded via Notion File Upload API |
| Unsupported format (e.g., SVG) | Replaced with placeholder text |
Local image paths are resolved relative to the Markdown file's location. When --image-base-dir is specified, paths starting with / are resolved relative to that directory instead. This is useful for static site generators like Hugo, where /image.png refers to a file in the static/ directory:
mdex export --dir ./content --image-base-dir ./static- Scan — Recursively find
.mdfiles in--dir, compute SHA-256 hashes - Schema — Ensure required properties exist in the Notion database
- Query — Fetch existing pages (filtered by domain if set)
- Plan — Compare local files vs. Notion pages:
- Create — New file, no matching page
- Update — Hash changed, archive old page and create new
- Delete — Page exists but file was removed, archive page
- Skip — Hash matches, no action needed
- Execute — Archive outdated pages, create new pages with blocks and images
See LICENSE for details.