Turn Markdown headings, paragraphs and lists into an editable Word DOCX document.
dotnet add package RecursiveAI.OneDocument.DocumentsRequires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.
using RecursiveAI.OneDocument.Documents; using var doc = Document.OpenMarkdown("notes.md"); doc.SaveAs("notes.docx"); Console.WriteLine("Wrote notes.docx");
Wrote notes.docxUnzip the example, then dotnet run.
notes.md is in the zip:
# Meeting notes A **bold** decision. - Ship the report - Archive the draft
The full program
The download’s Program.cs uses the supplied samples:
using RecursiveAI.OneDocument.Documents; using Document doc = Document.OpenMarkdown("notes.md"); doc.SaveAs("notes.docx"); Console.WriteLine("Wrote notes.docx"); foreach (Paragraph paragraph in doc.Paragraphs) Console.WriteLine($"[{paragraph.Style}] {paragraph.Text}");
dotnet run prints:
Wrote notes.docx [Heading 1] Meeting notes [Normal] A bold decision. [List Paragraph] Ship the report [List Paragraph] Archive the draft
Malformed Markdown can raise OfficeFormatException; resource limits raise OfficeLimitException.
Variations
Convert a Markdown string
using RecursiveAI.OneDocument.Documents; using var doc = Document.ParseMarkdown("# Hello\n\nA **bold** note."); doc.SaveAs("inline.docx"); Console.WriteLine("Wrote inline.docx");
Output:
Wrote inline.docxRead the import losses
Check ImportLoss.Items for content the importer could not carry over. The supplied notes.md has no import losses.
using RecursiveAI.OneDocument.Documents; using var doc = Document.OpenMarkdown("notes.md"); if (doc.ImportLoss is { } losses) { Console.WriteLine($"Import losses: {losses.Items.Count}"); foreach (var loss in losses.Items) Console.WriteLine($"{loss.Code}: {loss.Message}"); }
Output:
Import losses: 0Embed an image with a resolver
square.png is an 8 × 8 red square included in the zip. This example’s Markdown input is . The resolver supplies bytes only for that exact address; it does not fetch URLs or open arbitrary paths.
using RecursiveAI.OneDocument.Documents; var options = new MarkdownImportOptions { ImageResolver = address => address == "square.png" ? File.ReadAllBytes("square.png") : null }; using var doc = Document.ParseMarkdown( "", options); doc.SaveAs("illustrated.docx"); Console.WriteLine("Wrote illustrated.docx"); Console.WriteLine($"Import losses: {doc.ImportLoss!.Items.Count}");
Output:
Wrote illustrated.docx Import losses: 0
Related recipes
The short answer
Use Document.OpenMarkdown for a Markdown file, then save the document as .docx. For a string already in memory, use Document.ParseMarkdown, as shown in the variation. The resulting file contains editable Word paragraphs, runs and lists rather than a screenshot of the Markdown source.
OneDocument imports CommonMark with GitHub Flavored Markdown extensions. Its Markdown documentation lists the mapping between source elements and the document model, including the cases reported as import losses.
Can I convert a Markdown file to Word?
Yes. The example imports the heading as a heading paragraph, bold text as formatted runs, and the bullet list as a Word bullet list. The printed paragraph text does not include the ** markers: those control formatting in the DOCX. Likewise, a bullet is represented by document numbering, not necessarily by a literal bullet character in the paragraph text.
The importer handles headings, emphasis, strike-through, code spans and blocks, quotes, ordered and unordered lists, task-list marks, tables, links and footnotes. It is a document conversion rather than a reproduction of a website’s CSS. Code blocks become paragraphs in a source-code style, and their language information is dropped with a reported loss.
Markdown files must be named as Markdown explicitly. OpenMarkdown does that in the snippet; ParseMarkdown does it for in-memory text. A generic content sniffer does not recognize Markdown merely because a plain-text file happens to contain heading markers. Choosing the right entry point avoids treating Markdown syntax as literal document text.
How do I insert Markdown into Word?
Generate a DOCX from the Markdown with the full program, then open that output as a Word document. This approach converts markup to document structure before the user opens it. It is not an instruction to paste raw Markdown into an existing Word paragraph and expect that paragraph to interpret the syntax.
Images need an explicit resource policy. By default the importer does not read image paths or fetch image URLs. It embeds supported image bytes from a data URI or from a caller-supplied image resolver; otherwise it keeps the image description as text and records a loss. For a document that relies on diagrams or photos, review these losses before distributing the result.
Raw HTML is read through a sanitized fragment importer. Supported text and inline formatting can become document content, while scripts, styles, forms and unsupported structure are dropped and reported. This is why a Markdown document with HTML tables or custom styling may differ from its browser rendering.
The source sample on this page deliberately uses only a heading, a bold phrase and two list items, so its entire input is visible before you run the project. Start with that example, then inspect the import report and the output when adding images, HTML or other richer content. Review the features in the source, not just its .md extension.