Skip to main content
Glama

Markdown to Word

npm version npm downloads MCP registry CI License: MIT

Convert GitHub Flavored Markdown to a formatted Word (.docx) document, entirely in your browser, at md2word.app. Nothing is uploaded.

The same converter is on npm as markdown-to-word: a command-line tool, a Node library, and an MCP server for AI agents, no clone required:

npx markdown-to-word input.md

It's the mirror image of word2md.com (word-to-markdown-js), which goes the other way, and the successor to markdown_to_word, the original Ruby gem and web demo, first published in 2015 and now archived.

Usage

  1. Go to md2word.app.

  2. Drop a .md file anywhere on the page, choose one, or paste Markdown. AI chat responses (ChatGPT, Claude, and others) are Markdown, so they paste in as-is.

  3. A dropped file converts right away. For pasted text, click Convert to Word, or press Ctrl/⌘+Enter.

  4. Click Download .docx, or Copy formatted text to paste straight into Word or Google Docs with the formatting intact.

The document is named after its first heading, which also becomes its title.

Related MCP server: Word MCP

What converts

Markdown

In Word

# to ###### headings

Heading 1–6 styles

**bold**, *italic*, ~~strikethrough~~

The same formatting

Bulleted and numbered lists, nested

Word lists

- [x] task lists

☒ (done) and ☐ (to do) items, since Word can't show checkboxes from HTML

Tables

Thin borders, a bold header row that repeats on each page, and the column alignment from the Markdown

[links](https://example.com)

Clickable hyperlinks

`inline code` and fenced code blocks

Monospace; code blocks keep their line breaks

> block quotes

Left-aligned paragraphs, indented one step per level of nesting

--- horizontal rules

A line across the page

[^1] footnotes

Superscript numbers, with the notes as a numbered list after a line at the end of the document (not Word footnotes)

![images](https://example.com/a.png)

Embedded, when the browser can fetch them; otherwise the alt text, linked to the image (see Limitations)

<kbd>, <mark>, <abbr> and other inline HTML

The text, with <kbd> in the code font and <mark> highlighted

As on GitHub, a single newline inside a paragraph is joined into the same line, so hard-wrapped files convert to normal paragraphs. End a line with two spaces or a backslash to force a line break.

Optional Font Presets

Conversions use Word's standard styling by default. Choosing Chinese official document (公文) from the Style menu (always shown on the Chinese page, and elsewhere once the pasted text is mostly Chinese) rewrites the generated document to the layout of GB/T 9704-2012 《党政机关公文格式》. Each level gets an East Asian face and a 公文 size alongside Times New Roman:

Level

Font size

Chinese characters

Latin letters and digits

Body

三号 (16pt)

仿宋

Times New Roman

Heading 1

二号 (22pt)

方正小标宋简体

Times New Roman

Heading 2

三号 (16pt)

黑体

Times New Roman

Heading 3

三号 (16pt)

楷体

Times New Roman

Heading 4

三号 (16pt)

仿宋 (bold)

Times New Roman

Heading 5-6

三号 (16pt)

仿宋

Times New Roman

The preset also sets the page:

  • Paper and margins (5.1, 5.2.1): A4, with a 37mm top margin (天头) and a 28mm left margin (订口) around a 156 × 225mm text block (版心).

  • 22 lines of 28 characters (5.2.3): a Word document grid (文档网格) with exact line spacing at its pitch, 28.95pt. Widow control (孤行控制) is off, so it can't push a line to the next page and leave 21.

  • Indent (7.3.3): paragraphs, and the headings below the title, start two characters in (首行缩进 2 字符). Table cells, list items, and code blocks don't.

  • Title (7.3.1): Heading 1 is centered.

  • Spacing: 段前段后 0 行 on every level.

The app remembers your choice in this browser, and suggests the preset when the text you paste is mostly Chinese.

Word resolves a typeface per character class, so English words and numbers inside a Chinese sentence stay in Times New Roman. The fonts must be installed on the machine that opens the document. Some of the choices go beyond the standard:

  • The standard names no Latin font. Times New Roman for Latin letters and digits is common practice.

  • The standard says 小标宋体, not 方正小标宋简体 specifically. 方正小标宋简体 is a commercial Founder font that most machines don't have, so Word substitutes another font for the title unless it's installed.

  • Heading 4, the standard's third level (1.), is bold, which the standard doesn't ask for: it generally sets its third and fourth levels in plain 仿宋. Bolding that level is how these documents are written in practice. Headings 5 and 6 stay plain and rely on their numbers ((1) and ①) to stand out, as in a printed 公文.

Page numbers (7.5) aren't added yet; see #106. Neither are the red separator line and the other 版头 elements, which Markdown has no way to express.

Programmatically:

import {
  convertMarkdownToWord,
  CHINESE_OFFICIAL_DOC_FONTS,
} from 'markdown-to-word';

const blob = await convertMarkdownToWord(markdown, {
  fontPreset: CHINESE_OFFICIAL_DOC_FONTS,
});

pageMargins, when you also pass it, takes precedence over the preset's margins; the line grid is fitted to the margins the document ends up with.

Privacy

Conversion happens entirely in your browser: your Markdown and the Word document never leave your device. Images your Markdown links to load from the sites that host them, like on any web page. md2word.app uses cookie-free Cloudflare Web Analytics; a local or self-hosted copy has no analytics. Raw HTML in the preview is sanitized, so a shared file can't run script. See the privacy policy.

Limitations

  • Images are embedded only when the browser can fetch them: data: URIs, and web URLs whose server allows cross-origin requests (most image hosts and CDNs do). Any other image, including a local file path, becomes its alt text, linked to the image's URL when it has one.

  • Footnotes come out as a numbered list of notes at the end of the document, not as Word footnotes at the foot of each page.

  • Math stays as the literal text: $x^2$ comes out as $x^2$, not an equation.

  • Emoji shortcodes such as :tada: aren't converted; paste the emoji itself instead.

  • Links to headings in the same document ([see below](#usage)) don't jump anywhere in Word.

  • <details> sections are flattened: the summary and the hidden content both show, as ordinary paragraphs.

  • Complex raw HTML inside Markdown may not convert cleanly, and advanced Word features (comments, tracked changes, page layout) aren't produced.

  • Very large documents can be slow, since everything runs in the browser. Files are limited to 5 MB.

  • The site works in current versions of Chrome, Edge, Firefox, and Safari. The end-to-end tests run in Chrome.

Command line

Run it directly with npx (downloads and runs the latest published version):

npx markdown-to-word report.md

Or install it globally to get the md2w command:

npm install -g markdown-to-word
md2w report.md

It writes the .docx next to the input with the same name (report.md becomes report.docx), replacing any file already there, and prints where it wrote it to stderr. It needs Node 22.12 or later, and exits with status 1 and a message if the file is missing or empty.

To read the Markdown from stdin, pass - as the file, or leave it out and pipe the Markdown in. There's no file name to go on then, so -o is required:

cat reply.md | md2w -o reply.docx
md2w - -o - < report.md > report.docx

Options:

  • -o, --output <file>: write the .docx to <file>, creating folders as needed. -o - writes it to stdout.

  • --font-preset <name>: restyle the document with a font preset. chinese-official is the Chinese official document (公文) style described in Optional Font Presets.

  • -q, --quiet: print nothing to stderr except errors.

  • -V, --version: print the version.

MCP server

md2w mcp runs a local Model Context Protocol server on stdio, so AI agents can turn Markdown into Word documents on your machine. Like the website, it never uploads anything: the server converts in-process and writes the .docx to disk.

In Claude Desktop, it's one click: download markdown-to-word.mcpb and double-click it. Claude Desktop installs it as an extension. You don't need Node or a terminal, since Claude Desktop includes Node.

Add it to Claude Code:

claude mcp add markdown-to-word -- npx -y markdown-to-word mcp

Or to any MCP client that takes a JSON config:

{
  "mcpServers": {
    "markdown-to-word": {
      "command": "npx",
      "args": ["-y", "markdown-to-word", "mcp"]
    }
  }
}

It has one tool, convert_markdown_to_word:

  • markdown or path (exactly one): the Markdown as a string, or the path of a Markdown file (.md, .markdown, .mdown, or .txt). Prefer absolute paths; relative ones resolve against the server's working directory.

  • output_path (required): where to write the .docx; it must end in .docx. Missing folders are created.

  • overwrite: replace output_path if it already exists. Off by default, so the tool refuses to clobber an existing file and says so.

  • font_preset: chinese-official for the 公文 style; omit for Word's default styling.

It returns the output_path, the file's size in bytes, and any warnings (remote images it didn't download, or a .docx patch that had to be skipped), also as structured content.

An agent may be following instructions from a document or web page it read, so the tool limits what it can be talked into: it reads only Markdown files, writes only .docx files, never writes through a symlink, and never replaces a file unless asked. It also never downloads remote images, since a URL in a document could point anywhere, your own network included: they appear in the document as links. An agent that wants an image embedded can fetch it itself and put it in the Markdown as a data: URI. The CLI, run by you, has none of these limits and embeds remote images like the website.

In the browser, md2word.app registers the same conversion as a WebMCP tool, convert_markdown_to_docx (markdown, plus optional filename and font_preset), for agents in browsers that support it. It runs in the tab like a paste, shows the result on the page, and returns the .docx as base64. The page also tries to download the file, which a browser may block when no one clicked.

Use it from code

The converter is also on npm as markdown-to-word, for Node 22.12+ and for browsers through a bundler (Vite, webpack, esbuild):

npm install markdown-to-word

convertMarkdownToWord returns the .docx as a Blob:

import { writeFile } from 'node:fs/promises';
import { convertMarkdownToWord } from 'markdown-to-word';

const blob = await convertMarkdownToWord('# Hello\n\nFrom **Markdown**.', {
  pageMargins: { top: 1, right: 1, bottom: 1, left: 1 }, // inches
});
await writeFile('hello.docx', Buffer.from(await blob.arrayBuffer()));

In a browser, hand the Blob to a download link with URL.createObjectURL(blob).

The converter needs the DOM's DOMParser and XMLSerializer. Browsers have them; in Node, the package sets them as globals from jsdom when they're missing, so there's nothing to set up.

The package also exports parseMarkdownToHtml, extractTitle, the FONT_PRESETS (see Optional Font Presets), and their TypeScript types. parseMarkdownToHtml passes raw HTML through unsanitized, so sanitize its output (for example with DOMPurify) before putting it in a page.

On a server, treat Markdown from users as untrusted: the converter downloads each remote image (![](https://…)) to embed it, so the request comes from your server (up to 100 distinct http(s) URLs per document, 10 MB each, 4 at a time). Strip or allowlist image URLs first if the server can reach internal addresses.

Development

Requires Node.js 22.12 or later (.nvmrc pins the version CI uses).

git clone https://github.com/benbalter/markdown-to-word-js.git
cd markdown-to-word-js
npm install
npm run dev        # http://localhost:4321
npm run build      # static site in dist/
npm test           # unit tests; npm run test:e2e for Playwright

See CONTRIBUTING.md for the project layout and checks.

  • Site: Astro with Tailwind CSS, sharing word2md.com's design system.

  • Conversion: marked turns Markdown into HTML, @turbodocx/html-to-docx builds the .docx, and JSZip patches fix up code blocks, rules, table headers, and font presets.

  • Translations: the UI is ready for more languages; see web/i18n/README.md.

  • Hosting: Cloudflare Workers Static Assets. A push to main builds, runs the tests, and deploys; see docs/cloudflare.md.

Contributing

Contributions are welcome. See CONTRIBUTING.md, and open an issue for bugs and feature requests.

License

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers