Documentation
Everything the app does, and everything it cannot do.
Running an export
There are two ways in. Use the first when you are already looking at the page you want; use the second to take a whole space in one go.
From a page
- Open any Confluence page.
- Click ⋯ (More actions) at the top right, then Apps.
- Choose Export to Markdown.
- Pick a scope — this page and everything under it, or the whole space.
- Leave Include attachments ticked unless you only want the text.
- Click Export. A zip downloads when it finishes.
From the space sidebar
- Open the space.
- In the left sidebar, under Space apps, choose Export to Markdown.
- The scope is the whole space — there is no current page to narrow it to, so that option is not offered here.
- Click Export.
Not finding it? App entries live in two places and neither is obvious. On a page it is the ⋯ menu, under Apps — not the page toolbar. In a space it is the bottom of the left sidebar, under Space apps — you may need to scroll.
What the zip contains
The folder structure mirrors the page tree. A page with children becomes a file and a directory of the same name:
engineering-handbook.md
engineering-handbook/
├── getting-started.md
├── getting-started/
│ ├── install.md
│ └── advanced.md
├── reference.md
├── reference/
│ └── api.md
└── attachments/
├── diagram.png
└── 설계도.png
- Filenames keep their characters. A Korean, Japanese or Chinese title
stays readable. Only characters a filesystem refuses —
/ \ : * ? " < > |— are replaced, and two different titles never collapse onto the same file. - Attachments go into an
attachments/folder beside the page that owns them, and the body is rewritten to point at them relatively. - Links between exported pages become relative paths, so the export works on its own. Links to pages outside the selected scope stay as Confluence URLs — there is no file to point at.
How each piece of Confluence is converted
Kept exactly
| Confluence | Markdown |
|---|---|
| Task list, complete / incomplete | - [x] / - [ ] |
| Code block with a language | Fence carrying that language |
| Nested lists, to any depth | Nested lists, correctly indented |
| Code block or table inside a list item | Same, indented to stay inside the item |
| Bold, italic, strikethrough, inline code | The equivalent Markdown |
| Tables, including block content in cells | GitHub tables |
| Images, including external ones | , attachments exported |
| Page links, anchors, external links | Relative or absolute links |
| Emoji, CJK text, dates | Unchanged; dates as YYYY-MM-DD |
Degraded, with nothing lost
These have no Markdown equivalent. They are converted to the closest thing that keeps every word.
| Confluence | Becomes |
|---|---|
| Info / note / warning / tip panel | > [!NOTE], [!WARNING]… — an alert on GitHub, a quote elsewhere |
| Expand (collapsed content) | <details><summary>, body included |
| Column layout | The columns' blocks, in reading order |
| Merged table cells | A flat grid; the text lands once, the covered cells are blank |
| Status lozenge | Bold text |
| Underline, subscript, superscript | <u>, <sub>, <sup> |
| A macro with a body (including unknown ones) | Its body, under a comment naming the macro |
| A macro with no body (table of contents, children) | A comment naming the macro and its parameters |
| A draw.io or Gliffy diagram | An image pointing at the exported attachment |
What the app cannot do
- It does not import. This is a one-way export, Confluence to Markdown.
- It does not render PlantUML. No common Markdown renderer does, so the source is preserved as a code block rather than turned into a picture.
- It cannot export what Confluence did not store. A raw
<iframe>, for example, is discarded by Confluence Cloud when the page is saved — it is not on the page by the time any exporter sees it. - It cannot recover a macro's rendered output when that output lives outside the page. The macro's own content comes out; a picture generated on some vendor's server does not.
The degradation report
Every export ends with a summary of what had to be degraded and which page it came from. Expand it to see each item individually. If nothing had to be degraded, it says so.
This exists because the failures that matter in an export are the silent ones. A report you can read is the difference between a known trade-off and a surprise six months later.
Checking the output yourself
The conformance suite this app is built against is public, and it works on any exporter, not just this one.
git clone https://github.com/YoungjuneKwon/confluence-markdown-conformance
cd confluence-markdown-conformance
npm install
export CONFLUENCE_BASE_URL=https://your-site.atlassian.net
export CONFLUENCE_EMAIL=you@example.com
export CONFLUENCE_API_TOKEN=...
node scripts/seed.mjs # creates a space full of hard cases
node scripts/verify-seed.mjs # confirms Confluence stored all of it
# export that space with any app, unzip it, then:
node scripts/score.mjs out/your-export
The rules and the reasoning behind each one are in SPEC.md. If you think a rule is wrong, it is a file you can argue with.
Frequently asked
How large a space can it handle?
Measured on a real Confluence Cloud site: a space of 1,000 pages exports in under four minutes, and 349 MB of attachments in about fourteen — both with nothing degraded and an archive that passes an integrity check.
The work happens in your browser, so the practical limit is memory rather than a server timeout. We have not found that limit yet. If you do hit one, export a subtree at a time, or untick Include attachments and fetch those separately.
Does it need admin permissions?
No. It reads as you, with your own permissions, and asks only for read access. You cannot export a page you could not already open.
Why is a page link to another space still a URL?
Because there is no file in the export to point at. Rewriting it to a relative path would produce a link that leads nowhere, which is worse than a link that works in the browser.
Can I re-import the Markdown into Confluence?
This app does not import, but the output is ordinary CommonMark plus GitHub extensions, so any Markdown importer can read it. Task state and code block languages survive the round trip, which is the part importers usually cannot reconstruct on their own.