C#
Word Document
Programming
.NET
Automation

How can a Word document be created in C?

Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms

Introduction

To create a Word document in C#, the best approach for most scenarios is the Open XML SDK (DocumentFormat.OpenXml), which generates .docx files without requiring Microsoft Word to be installed. For simpler needs, the DocX library (Xceed.Document.NET) provides a fluent API. IronWord offers a similar high-level API as a commercially supported, cross-platform option. Microsoft Office Interop (Microsoft.Office.Interop.Word) offers the most features but requires Word to be installed and is not suitable for server-side applications. Choose Open XML SDK for server/cloud deployments and Interop for desktop automation.

csharp
1// Install: dotnet add package DocumentFormat.OpenXml
2
3using DocumentFormat.OpenXml;
4using DocumentFormat.OpenXml.Packaging;
5using DocumentFormat.OpenXml.Wordprocessing;
6
7public void CreateDocument(string filePath)
8{
9    using var doc = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document);
10
11    // Add main document part
12    var mainPart = doc.AddMainDocumentPart();
13    mainPart.Document = new Document();
14    var body = mainPart.Document.AppendChild(new Body());
15
16    // Add a heading
17    var heading = new Paragraph(
18        new ParagraphProperties(
19            new ParagraphStyleId() { Val = "Heading1" }
20        ),
21        new Run(new Text("Monthly Report"))
22    );
23    body.AppendChild(heading);
24
25    // Add a paragraph with formatted text
26    var para = new Paragraph(
27        new Run(
28            new RunProperties(new Bold()),
29            new Text("Summary: ")
30        ),
31        new Run(new Text("All metrics are within expected ranges."))
32    );
33    body.AppendChild(para);
34
35    // Add a bulleted list
36    string[] items = { "Revenue up 12%", "Costs down 5%", "Customer satisfaction at 94%" };
37    foreach (var item in items)
38    {
39        var listItem = new Paragraph(
40            new ParagraphProperties(
41                new NumberingProperties(
42                    new NumberingLevelReference() { Val = 0 },
43                    new NumberingId() { Val = 1 }
44                )
45            ),
46            new Run(new Text(item))
47        );
48        body.AppendChild(listItem);
49    }
50
51    mainPart.Document.Save();
52}

Adding a Table with Open XML

csharp
1public void AddTable(Body body)
2{
3    var table = new Table();
4
5    // Table properties (borders)
6    var tblProperties = new TableProperties(
7        new TableBorders(
8            new TopBorder() { Val = BorderValues.Single, Size = 4 },
9            new BottomBorder() { Val = BorderValues.Single, Size = 4 },
10            new LeftBorder() { Val = BorderValues.Single, Size = 4 },
11            new RightBorder() { Val = BorderValues.Single, Size = 4 },
12            new InsideHorizontalBorder() { Val = BorderValues.Single, Size = 4 },
13            new InsideVerticalBorder() { Val = BorderValues.Single, Size = 4 }
14        )
15    );
16    table.AppendChild(tblProperties);
17
18    // Header row
19    var headerRow = new TableRow();
20    foreach (var header in new[] { "Name", "Department", "Salary" })
21    {
22        var cell = new TableCell(
23            new Paragraph(new Run(
24                new RunProperties(new Bold()),
25                new Text(header)
26            ))
27        );
28        headerRow.AppendChild(cell);
29    }
30    table.AppendChild(headerRow);
31
32    // Data rows
33    var data = new[] {
34        ("Alice", "Engineering", "$120,000"),
35        ("Bob", "Marketing", "$95,000"),
36    };
37
38    foreach (var (name, dept, salary) in data)
39    {
40        var row = new TableRow();
41        row.AppendChild(new TableCell(new Paragraph(new Run(new Text(name)))));
42        row.AppendChild(new TableCell(new Paragraph(new Run(new Text(dept)))));
43        row.AppendChild(new TableCell(new Paragraph(new Run(new Text(salary)))));
44        table.AppendChild(row);
45    }
46
47    body.AppendChild(table);
48}

DocX Library (Xceed) — Simpler API

csharp
1// Install: dotnet add package DocX
2
3using Xceed.Document.NET;
4using Xceed.Words.NET;
5
6public void CreateWithDocX(string filePath)
7{
8    using var doc = DocX.Create(filePath);
9
10    // Title
11    doc.InsertParagraph("Monthly Report")
12       .FontSize(24)
13       .Bold()
14       .Alignment = Alignment.center;
15
16    // Body text
17    doc.InsertParagraph("All metrics are within expected ranges.")
18       .FontSize(12)
19       .SpacingAfter(10);
20
21    // Bulleted list
22    var list = doc.AddList("Revenue up 12%", 0, ListItemType.Bulleted);
23    doc.AddListItem(list, "Costs down 5%");
24    doc.AddListItem(list, "Customer satisfaction at 94%");
25    doc.InsertList(list);
26
27    // Table
28    var table = doc.AddTable(3, 3);
29    table.Rows[0].Cells[0].Paragraphs[0].Append("Name").Bold();
30    table.Rows[0].Cells[1].Paragraphs[0].Append("Department").Bold();
31    table.Rows[0].Cells[2].Paragraphs[0].Append("Salary").Bold();
32    table.Rows[1].Cells[0].Paragraphs[0].Append("Alice");
33    table.Rows[1].Cells[1].Paragraphs[0].Append("Engineering");
34    table.Rows[1].Cells[2].Paragraphs[0].Append("$120,000");
35    doc.InsertTable(table);
36
37    doc.Save();
38}

IronWord: High-Level API with Styling and Tables

IronWord is a commercial library from Iron Software that sits between the two styles above: like DocX it hides the Open XML plumbing behind a short, object-based API, and like the Open XML SDK it needs no Word installation, so it runs on Windows, Linux, macOS, Docker and cloud hosts. It targets .NET 8+, .NET Standard 2.0 and .NET Framework, and styling, tables and images are all handled through plain model objects.

csharp
1// Install: dotnet add package IronWord
2
3using IronWord;
4using IronWord.Models;
5
6public void CreateWithIronWord(string filePath)
7{
8    // License.LicenseKey = "YOUR-KEY"; // trial keys work without watermarks
9
10    var doc = new WordDocument();
11
12    // Title: a styled text run inside a paragraph
13    var title = new TextContent("Monthly Report")
14    {
15        Style = new TextStyle { IsBold = true, FontSize = 24 }
16    };
17    doc.AddParagraph(new Paragraph(title));
18
19    // Body text
20    doc.AddText("All metrics are within expected ranges.");
21
22    // Table: 3 rows x 3 columns, addressed by [row, column]
23    var table = new Table(3, 3);
24    string[,] cells =
25    {
26        { "Name", "Department", "Salary" },
27        { "Alice", "Engineering", "$120,000" },
28        { "Bob", "Marketing", "$95,000" },
29    };
30    for (int r = 0; r < 3; r++)
31        for (int c = 0; c < 3; c++)
32            table[r, c] = new TableCell(new TextContent(cells[r, c]));
33    doc.AddTable(table);
34
35    doc.SaveAs(filePath);
36}

The same WordDocument class also opens existing files (new WordDocument("existing.docx")), so editing a template and generating a fresh document use one API. IronWord needs a license key in production; a free trial key is available, and paid licenses are perpetual.

Microsoft Office Interop (Desktop Only)

csharp
1// Requires Microsoft Word installed
2// Add reference: Microsoft.Office.Interop.Word
3
4using Word = Microsoft.Office.Interop.Word;
5
6public void CreateWithInterop(string filePath)
7{
8    var app = new Word.Application();
9    var doc = app.Documents.Add();
10
11    try
12    {
13        // Add title
14        var titlePara = doc.Paragraphs.Add();
15        titlePara.Range.Text = "Monthly Report";
16        titlePara.Range.Font.Size = 24;
17        titlePara.Range.Font.Bold = 1;
18        titlePara.Range.InsertParagraphAfter();
19
20        // Add body text
21        var bodyPara = doc.Paragraphs.Add();
22        bodyPara.Range.Text = "All metrics are within expected ranges.";
23        bodyPara.Range.Font.Size = 12;
24        bodyPara.Range.Font.Bold = 0;
25        bodyPara.Range.InsertParagraphAfter();
26
27        doc.SaveAs2(filePath);
28    }
29    finally
30    {
31        doc.Close();
32        app.Quit();
33
34        // Release COM objects
35        System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
36        System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
37    }
38}

Comparison

FeatureOpen XML SDKDocX (Xceed)IronWordOffice Interop
Word requiredNoNoNoYes
Server-safeYesYesYesNo
API complexityVerboseSimpleSimpleMedium
.docx supportYesYesYesYes
.doc supportNoNoNoYes
NuGet packageDocumentFormat.OpenXmlDocXIronWordN/A (COM)
LicenseMITFree (community)Commercial (free trial)Office license

Common Pitfalls

  • Using Office Interop on a server: Microsoft.Office.Interop.Word requires a full Word installation and launches a Word process. It is not thread-safe, leaks memory if COM objects are not released, and Microsoft explicitly does not support server-side Interop. Use Open XML SDK or DocX for server applications.
  • Not disposing WordprocessingDocument: WordprocessingDocument implements IDisposable. Failing to call Dispose() (or use using) leaves the file locked and may produce a corrupted .docx file. Always wrap in a using statement.
  • Open XML text spaces being collapsed: By default, Open XML strips leading/trailing spaces from Text elements. To preserve spaces, set Space = SpaceProcessingModeValues.Preserve on the Text element: new Text(" indented") { Space = SpaceProcessingModeValues.Preserve }.
  • Missing numbering definitions for lists: Open XML bulleted/numbered lists require a NumberingDefinitionsPart with abstract and concrete numbering definitions. Without this, list items render as plain paragraphs. The DocX library handles this automatically.
  • Forgetting to release COM objects with Interop: Each Interop call creates COM objects that must be released with Marshal.ReleaseComObject(). Failing to release them keeps Word processes running in the background. Always release in a finally block and call GC.Collect() after releasing.

Summary

  • Use Open XML SDK for server/cloud applications — no Word installation required, full .docx control
  • Use DocX (Xceed) for a simpler API when complex Open XML structures are not needed
  • Use IronWord for a simple, cross-platform API with commercial support and built-in styling, tables and images
  • Use Office Interop only for desktop applications that need full Word feature access (track changes, macros)
  • Always dispose WordprocessingDocument with using to prevent file corruption
  • Never use Office Interop on servers — it is unsupported, not thread-safe, and leaks resources

Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms