If you have ever pasted an article into Notion and watched the headings collapse into body text, the code turn into a wall of unstyled lines, and a table become tab-separated mush, the problem usually was not Notion. It was the Markdown you handed it. This post is a precise account of how importing Markdown into Notion actually works: what the paste path converts, what the file importer keeps, where each one loses fidelity, and how to produce input that survives the trip. The mechanics here pair directly with the "copy for Notion" flow in BulkMD, which exists specifically to hand Notion the shape it parses cleanly.
We will cover the two distinct import paths Notion gives you, a block-by-block fidelity table, the frontmatter and nesting traps, why none of this requires the Notion API, and a short checklist for reliable imports.
How does Notion turn Markdown into blocks
Notion is not a Markdown editor. Its document model is a tree of typed blocks (paragraph, heading_1, bulleted_list_item, code, table, and so on), each with its own properties. There is no Markdown stored anywhere underneath. So every time Markdown enters Notion, something has to translate the text syntax into that block tree. There are two translators, and they behave differently.
The first is the paste parser. When you paste text into a Notion page, Notion inspects the clipboard. If it detects Markdown-shaped syntax (a # at line start, a fenced code block, a - list), it offers to interpret the paste as Markdown and builds blocks from it. This is the path most people hit by accident, and it is the one that produces the "why is everything plain text" complaints, because the detection is all-or-nothing per paste, and it is sensitive to how the source formatted its line breaks.
The second is the file importer: it is the Import button in the left sidebar, then Markdown & CSV (or Text & Markdown). This path reads a .md file from disk and creates a new page from it. It uses a more deliberate parser than the live paste, and notably it handles GFM tables, where paste often does not.
Why paste sometimes lands as plain text
The paste parser only converts when it is confident the clipboard is Markdown. Two things commonly defeat that confidence. First, rich-text clipboards win over plain text: if you copy from a rendered web page, your clipboard carries HTML, and Notion's HTML paste path runs instead of the Markdown path, sometimes well, sometimes not. Second, soft line breaks inside paragraphs can make a Markdown document read as one long run, so headings mid-stream do not get recognized.
The reliable trick is to paste as plain text first (Cmd/Ctrl+Shift+V on most platforms) so Notion sees raw Markdown characters, then let the Markdown detection fire. BulkMD's "copy for Notion" puts clean Markdown, not HTML, on the clipboard for exactly this reason: it removes the ambiguity about which paste path runs.
What survives the conversion, and what does not
Here is the block-by-block reality. The two columns reflect the two paths, because they genuinely differ. Treat this as a working reference rather than a promise: Notion's parser changes over time, and your mileage varies with the exact Markdown.
| Markdown element | Paste into page | Import as .md file |
|---|---|---|
Headings (#–###) | Maps to heading_1/2/3; H4+ flattens to H3 | Same; H4+ flattens to H3 |
| Bold / italic / inline code | Preserved as rich-text annotations | Preserved |
| Bulleted / numbered lists | Preserved | Preserved |
| Nested lists | Often flattens one level | More reliable, still imperfect |
| Fenced code blocks | Becomes a code block; language hint usually kept | Code block; language kept |
Blockquotes (>) | Becomes a quote block | Quote block |
| Links | Preserved | Preserved |
Images (![]()) | Remote URL becomes an embed/bookmark | Local image paths break unless zipped |
| GFM tables | Frequently lands as plain text or a code block | Becomes a real Notion table |
Horizontal rule (---) | Becomes a divider | Divider |
Task lists (- [ ]) | Becomes to-do blocks | To-do blocks |
Frontmatter (--- YAML) | Rendered as literal text | Ignored or shown as text, not properties |
| Footnotes | Not supported; rendered inline | Not supported |
The pattern is consistent: flat, well-formed structure survives; deep nesting and tables are where the two paths diverge. If your document is mostly headings, paragraphs, lists, code, and quotes, paste is fine and instant. If it contains tables you care about, use the file importer.
The table problem in detail
A GFM table is plain text that looks tabular only because of pipes and a dashed separator row:
| Model | Tokenizer | Context window |
| --- | --- | --- |
| GPT-4o | o200k_base | 128k |
| Claude | proprietary | 200k |
The live paste parser frequently does not recognize this as a table and drops it in as a paragraph or a code block, and you lose the grid. The file importer does recognize it and builds a native Notion table with real cells. So the rule is blunt: if the document has tables, save it as .md and import the file; do not paste. This is the single biggest fidelity decision in the whole workflow.
When to paste and when to import a file
These are not interchangeable. They produce different artifacts and have different failure modes.
Paste into an existing page when you want the content to live inside a page you already have: appended to running notes, dropped into a database row's page body, or merged with other blocks. It is immediate, it requires no file on disk, and for prose-heavy content it is lossless enough. Its cost is the table problem and occasional nesting flattening.
Import a .md file when you want one new page per source, when tables matter, or when you are bringing in many documents at once. The importer creates a fresh page titled from the filename (or the first H1), which is exactly what you want for a batch of captured articles. Its costs: it ignores frontmatter, it cannot merge into an existing page, and local image references break unless you import a zip that contains the images.
A useful heuristic: paste is for "add this to a page I'm working in"; file import is for "make these into pages." If you are building a reading archive or a research database where each source is its own entry, the file importer is the correct tool, and it scales: you can import many files in one operation.
Bringing in many pages at once
For a batch, say the output of a bulk capture run, the file importer accepts multiple .md files in a single import and creates a page for each. This is where producing clean Markdown up front pays off most: fifty files that each have one clean heading tree and GFM tables import into fifty tidy pages. The same captured files also drop straight into an Obsidian vault, which is why the Obsidian knowledge base workflow and a Notion archive can share the exact same source folder. If you want those files to carry structured metadata, the frontmatter for web clippings guide covers what to write, just be aware Notion will not read that YAML as properties on import.
You do not need the Notion API for any of this
A recurring misconception is that getting content into Notion programmatically requires the Notion API, an integration token, and a developer setup. For import, it does not. Paste and file import are ordinary account features: you are logged into Notion, you paste or you click Import, and you are done. No OAuth, no token, no integration shared with a workspace.
The Notion API exists for a different job: programmatically creating and updating blocks and database entries from a server, with the well-known constraints that come with it, including an average rate limit on the order of three requests per second per integration, a cap of roughly 100 blocks per create call, and a token your code has to hold that a workspace admin has to authorize. That is the right tool if you are building an automated pipeline that writes into Notion continuously. It is the wrong tool, and a lot of unnecessary setup, if you just want today's reading into a page.
This mirrors BulkMD's broader stance: the product is local-only, with no account and no telemetry, and the clipboard is the integration surface. Putting clean Markdown on the clipboard means any destination that understands paste (Notion, Obsidian, an LLM chat box) works without a single credential. The one network feature in BulkMD, optional AI summarize/clean, is off by default and uses your own API key; nothing about importing into Notion touches it.
The single decision that prevents most formatting loss
For prose-heavy pages, pasting clean Markdown into Notion preserves headings, lists, code, and quotes with near-zero loss, while tables survive only through the file importer, so the single highest-leverage move is to decide paste vs. import by one question: does this document contain a table I care about? That one branch accounts for the large majority of "Notion ate my formatting" reports, and it is fully under your control before you ever touch Notion.
What clean Markdown looks like for Notion
The common failure mode is not Notion's parser; it is messy Markdown produced by copying rendered HTML. A page run through a readability extractor and a faithful HTML-to-Markdown serializer produces input both Notion paths handle well. A "Notion-clean" document looks like this end to end:
# Transformer Attention, Explained
A single coherent title becomes the page heading; sections use real
heading levels instead of bold paragraphs faking structure.
## Why scaled dot-product attention
Plain prose paragraphs map straight to paragraph blocks. **Bold** text
and `inline code` survive as rich-text annotations on the same block.
- One flat list level imports cleanly
- A second level is where paste starts to flatten
A fenced code block here (with a language hint such as python) becomes
a single Notion code block, not a run of unstyled paragraph lines.
| Symbol | Meaning |
| --- | --- |
| q | query matrix |
| k | key matrix |
Everything in that example is the shape Notion parses without a fight. Concretely, "Notion-clean" Markdown means:
- One coherent heading tree. Article title as a single top-level heading, real
##/###for sections, not a dozen######artifacts from a site's nav and footer. - Fenced code blocks with language hints, so Notion assigns the code block its language.
- GFM pipe tables (the format the file importer recognizes), not HTML
<table>markup left half-converted. - Absolute image URLs, so paste can turn them into embeds rather than producing broken local paths.
- No leftover boilerplate (cookie banners, share widgets, related-posts rails) that otherwise becomes dozens of junk blocks you delete by hand.
This is the same clean-extraction problem that matters for feeding pages to an LLM as context: the boilerplate that inflates token counts is the same boilerplate that clutters a Notion page. A boilerplate-heavy page stripped to its article core commonly drops 60–80% of its tokens, and the bytes you remove are exactly the blocks you would have deleted in Notion by hand. Solve it once at extraction time and both destinations benefit. BulkMD's pipeline (Readability for the main content, Turndown for faithful Markdown, GFM tables on) is tuned to emit exactly the shape Notion's importer parses, and the "copy for Notion" action skips the file step entirely for single pages.
TL;DR: the reliable import recipe
Decide by content type, then act. If the page is prose, lists, and code: copy clean Markdown to the clipboard, open the target Notion page, paste as plain text, and let Markdown detection build the blocks. If the page has tables you care about, or you are importing many sources at once: save clean .md files and use Notion's Import button so tables become real tables and each file becomes its own page. Either way, the determining factor is the quality of the Markdown you start with: flat heading tree, fenced code, GFM tables, no boilerplate. Generate that with BulkMD from the Chrome Web Store, use "copy for Notion" for single pages and the bulk export for batches, and the import stops eating your formatting.
Frequently asked questions
Why does my pasted Markdown show up as plain text in Notion?
How do I get a Markdown table into Notion without it breaking?
Does importing Markdown into Notion require the Notion API or a token?
What happens to YAML frontmatter when I import into Notion?
Can I import many Markdown files into Notion at once?
About the author
Independent software engineer building developer tools at Soft Web Grove. Creator and maintainer of BulkMD.
Reach the team at [email protected] — typically within 24 hours, any day of the year. Soft Web Grove also takes a small number of outside engagements; details on the about page.