---
name: ironword
description: >
  Create, edit, read and convert Word DOCX files in C#/.NET using IronWord (the `IronWord`
  NuGet package) — no Microsoft Office or Word Interop required. Use when the task involves
  generating a .docx from scratch, editing an existing Word document, adding or styling
  paragraphs/text runs (bold, italic, color, font), building tables with borders/merged
  cells/zebra striping, inserting images, page setup (paper size, orientation, margins),
  numbered/bulleted lists, mail merge (dictionary, DataTable/DataRow, repeating regions),
  finding/replacing text, extracting text or images from a DOCX, or converting DOCX to PDF —
  or whenever a project already references `IronWord`, `WordDocument`, `TextContent`, or
  `IronWord.Models`.
---

# IronWord (C# / .NET)

IronWord is a pure managed .NET library (built on OpenXML, no Chromium/Tesseract-style native
engine) that creates, loads, edits and saves `.docx` files. It runs on Windows, Linux and
macOS with no Microsoft Word, Office, or Interop dependency, and no platform-specific native
binaries to install.

## Scope of this skill

| | |
|---|---|
| Package | `IronWord` (single package — no platform variants) |
| Versions | 2024.x – 2026.x (verified against 2026.8.1) |
| Namespaces | `IronWord`, `IronWord.Models`, `IronWord.Models.Enums`, `IronWord.Models.MailMerge`, `IronWord.Models.List` |
| Runtimes | .NET Framework 4.6.2+, .NET Standard 2.0+, .NET Core 2.0+/3.1, .NET 5–10 |

## Install

```bash
dotnet add package IronWord
```

There is only one package to choose — IronWord ships a single `netstandard2.0` managed
assembly with no native/platform-specific binaries, so (unlike IronPDF or IronOCR) there is no
Linux/macOS/Windows variant to pick and nothing to bake into a container image for the library
itself.

## Licensing — do this first, every time

```csharp
IronWord.License.LicenseKey = Environment.GetEnvironmentVariable("IRONWORD_LICENSE_KEY");
if (!IronWord.License.IsLicensed)
    Console.Error.WriteLine("IronWord is unlicensed — production save calls will throw.");
```

Rules:

- Read the key from the environment (`IRONWORD_LICENSE_KEY`) or user secrets. **Never** inline
  a key in source, never write one into a file you commit, never echo one to the terminal.
- **Treat a missing key as a blocker, not a warning.** Verified behaviour: with no key set,
  `WordDocument.SaveAs(...)` throws `IronSoftware.Exceptions.LicensingException: Production
  License Required`, whose message states a **7-day free development grace period** before
  production calls are refused outright — this is not a watermark, it is a hard failure.
  Separately, trial keys issued from the licensing page unlock 30 days of full-featured use.
  If `IsLicensed` is false, say so and ask the user for a key rather than assuming output will
  save. Trial keys: <https://ironsoftware.com/csharp/word/licensing/>.
- `IronWord.License.IsValidLicense(key)` checks a key without applying it.
  `IronWord.License.AssertLicense(key)` throws immediately if the key doesn't validate.
- Alternative to setting the property in code: `Web.Config`/`App.Config`
  (`<add key="IronWord.LicenseKey" value="..."/>` in `appSettings`) for .NET Framework, or
  `appsettings.json` (`"IronWord.LicenseKey": "..."`) for .NET Core/.NET.
- `IronWord.License.DisableAppAnalytics()` turns off anonymous usage analytics.

## Running a one-off task from the terminal

Requires only the .NET SDK. On **.NET 10+**, a single file is the whole program:

```bash
cat > /tmp/task.cs <<'EOF'
#:package IronWord@2026.8.1
using IronWord;
IronWord.License.LicenseKey = Environment.GetEnvironmentVariable("IRONWORD_LICENSE_KEY");
var doc = new WordDocument();
doc.AddText("Hello, World!");
doc.SaveAs("out.docx");
EOF
dotnet run /tmp/task.cs
```

Unlike IronPDF/IronOCR, **`#:property PublishAot=false` is not needed** — IronWord is pure
managed code (OpenXML + SixLabors.ImageSharp) with no runtime code-generation requirement;
verified running as a file-based app with no AOT-related exception.

On older SDKs, use a scratch project:

```bash
dotnet new console -o /tmp/ironword-scratch && cd /tmp/ironword-scratch
dotnet add package IronWord
# write Program.cs, then:
dotnet run
```

## Recipes

Every call below was compiled and run against the shipped `IronWord.dll`/`IronWord.xml`
(v2026.8.1) to confirm the exact member names and signatures.

### Create, load and save a document

```csharp
var doc = new WordDocument();                    // new blank document
var fromFile  = new WordDocument("input.docx");   // load from path
var fromBytes = new WordDocument(File.ReadAllBytes("input.docx"));

doc.AddText("Hello, World!");                     // shorthand: appends a plain paragraph

doc.SaveAs("out.docx");                            // save to path
byte[] bytes = doc.Save();                         // save to a byte[] (e.g. for HTTP response)
doc.Save("out2.docx");                             // save to a path (overload of Save)
string json = doc.ToJson();                        // serialize the document model to JSON
```

### Paragraphs and styled text runs

`TextContent`'s own `Color`/`FontSize`/`IsBold`/etc. properties are **read-only** — they report
the *effective* style, they don't set it. To style text, set `TextContent.Style` (or wrap it in
a `Run` and set `Run.Style`) to a `TextStyle`:

```csharp
var paragraph = new Paragraph();
var text = new TextContent("Styled text");
var style = new TextStyle
{
    IsBold = true,
    FontSize = 14,
    Color = Color.Red,                     // IronWord.Models.Color — has named colors + FromArgb
    TextFont = new Font { FontFamily = "Arial" }
};
var run = new Run(text) { Style = style };
paragraph.AddRun(run);
doc.AddParagraph(paragraph);

// Shortcuts directly on Run (subset of TextStyle): IsBold, IsItalic, Color are settable.
var run2 = new Run(new TextContent("Quick bold")) { IsBold = true, Color = Color.Blue };
```

`Paragraph` also has `SetStyle(ParagraphStyle)` and `SetAlignment(TextAlignment)` (both return
`Paragraph`, so they chain), plus direct properties: `LineSpacing`, `SpacingBefore`,
`SpacingAfter`, `FirstLineIndentation`, `HorizontalContentAlignment`.

### Tables

```csharp
var table = new Table(3, 3);                        // rows, columns
table[0, 0] = new TableCell(new TextContent("Number"));
for (int i = 1; i < table.Rows.Count; i++)
    table[i, 0] = new TableCell(new TextContent($"{i}"));

var borderStyle = new BorderStyle
{
    BorderColor = Color.Black, BorderValue = BorderValues.Thick, BorderSize = 5
};
table.Borders = new TableBorders
{
    TopBorder = borderStyle, RightBorder = borderStyle,
    BottomBorder = borderStyle, LeftBorder = borderStyle
};
table.Zebra = new ZebraColor("FFFFFF", "dddddd");    // alternating row fill, hex without '#'
doc.AddTable(table);
```

`table[row]` indexes a `TableRow`; `table[row, col]` indexes a `TableCell` — both have getters
*and* setters. Other verified members: `table.MergeCells(r1, c1, r2, c2)`, `table.GetCell(r,
c)`, `table.GetRow(r)`, `table.AddRow(TableRow)`, `table.AddColumn()`,
`table.ApplyCellStyle(TextStyle, r1, c1, r2, c2)`, `TableCell.Split(rows, cols)`. `TableCell`
also has a `new TableCell("plain text")` shorthand constructor.

`BorderValues` options: `None`, `Single`, `Thick`, `Double`, `Dotted`, `Dashed`, `Wave`,
`ThreeDEmboss`, `ThreeDEngrave`, and several `*SmallGap`/`*MediumGap`/`*LargeGap` combinations.

### Images

```csharp
var image = new ImageContent("logo.png");           // also ctor(Stream), ctor(AnyBitmap)
image.Width = 100;                                    // double, in points
image.Height = 100;
paragraph.AddImage(image);
doc.AddImage("logo.png");                             // shorthand: appends as its own paragraph
```

A missing or unreadable file throws `System.IO.FileNotFoundException` from `ImageContent`'s
constructor (verified) — check the path before loading rather than assuming an empty image.
Other verified members: `SetDistanceFromTop/Bottom/Left/Right(double, MeasurementUnit)`,
`TextWrapBehavior`, `ZOrder`. Supported `ImageType`s: `Bmp`, `Gif`, `Png`, `Tiff`, `Icon`,
`Pcx`, `Jpeg`, `Emf`, `Wmf`, `Svg`.

### Page setup and sections

```csharp
doc.AddSection();                       // void — adds a default DocumentSection
var section = doc.Sections[0];          // read it back via the Sections list
section.PageSetup.Orientation = PageOrientation.Landscape;   // Portrait | Landscape
section.PageSetup.PaperSize = PaperSize.A4;                  // A4 | A5 | Letter | Legal | Custom
section.PageSetup.SetTopMargin(20, MeasurementUnit.Millimeter);
```

`MeasurementUnit` options: `Inch`, `Centimeter`, `Millimeter`, `EnglishMetricUnit`, `Twip`,
`Point`. `AddSection()`/`AddSection(DocumentSection)` both return `void` — always read the
section back from `doc.Sections` rather than expecting a return value.

### Numbered and bulleted lists

```csharp
var itemParagraph = new Paragraph();
itemParagraph.AddText("First item");

var list = new MultiLevelTextList();
list.AddItem(new ListItem(itemParagraph));
doc.AddMultiLevelTextList(list);
```

`MultiLevelTextList.ListType` (an enum with ~60 numbering-format values, e.g. `Decimal`,
`Bullet`, `UpperRoman`, `LowerLetter`) controls the numbering style;
`StartIndentation`/`HangingIndentation` control layout.

### Find, replace, extract

```csharp
string all = doc.ExtractText();
string firstParagraph = doc.Paragraphs[0].ExtractText();
string cellText = ((TableCell)table[0, 0]).ExtractText();

doc.ReplaceText("Hello", "Hi");                 // whole-document find/replace
TextContent found = doc.FindText("Hi");
List<AnyBitmap> images = doc.ExtractImages();   // IronSoftware.Drawing.AnyBitmap
```

`doc.ExtractTextFromPage(int)` / `doc.ExtractTextFromPages(IEnumerable<int>)` and
`doc.PageCount` are also available for page-scoped reads.

### Mail merge

```csharp
using IronWord.Models.MailMerge;

var template = new WordDocument("template.docx");

template.MailMerge.Execute(new Dictionary<string, string>
{
    { "FirstName", "Jane" }, { "LastName", "Smith" }
});

// Also verified: Execute(IEnumerable<string> names, IEnumerable<string> values),
// Execute(DataRow), Execute(DataTable), ExecuteWithRegions(DataSet),
// ExecuteWithRegions(DataTable), ExecuteWithRegions(string regionName, DataTable).

template.MailMerge.Options.RemoveUnusedFields = false;   // MailMergeOptions
IReadOnlyList<string> fields  = template.MailMerge.GetFieldNames();
IReadOnlyList<string> regions = template.MailMerge.GetRegionNames();

template.SaveAs("output.docx");
```

`MailMergeOptions` also has `RemoveUnusedRegions`, `NullValueReplacement`,
`CaseInsensitiveFieldNames`.

## Working with IronPDF

`WordDocument.ToPdf(string fileName)` exists and converts the document to PDF in place, but its
own XML documentation marks it **`EXPERIMENTAL API!`** and states it **"Requires IronPDF to
work"** — i.e. the project must also reference the `IronPdf` package for this call to succeed.
For production DOCX→PDF conversion, prefer the documented, non-experimental path through
IronPDF itself (see the **ironpdf** skill):

```csharp
new IronPdf.DocxToPdfRenderer().RenderDocxAsPdf("document.docx").SaveAs("document.pdf");
```

## When something fails

| Symptom | Cause and fix |
|---|---|
| `LicensingException: Production License Required` on `SaveAs` | No valid licence, and the 7-day development grace period has lapsed. Set `IronWord.License.LicenseKey` before any save. |
| `CS0200`-style "cannot be assigned to" on `TextContent.IsBold`/`.FontSize`/`.Color` | Those properties are read-only on `TextContent`. Set styling via `TextContent.Style = new TextStyle{...}` or wrap the text in a `Run` and set `Run.Style`/`Run.IsBold`/`Run.Color`. |
| `AddSection()` result can't be assigned | Both `AddSection()` and `AddSection(DocumentSection)` return `void`. Read the new section back from `doc.Sections[...]`. |
| `FileNotFoundException: Error loading image from '...'` | `ImageContent`'s file-path constructor throws immediately if the path is wrong — verify the path exists before constructing it. |
| Assembly-load / version-mismatch errors mentioning `DocumentFormat.OpenXml` | IronWord's own `DocumentFormat.OpenXml` reference conflicts with a direct reference the project also has. See the docs troubleshooting page ("DocumentFormat.OpenXml Conflicts") and align versions or remove the redundant direct reference. |
| Header/footer content missing after `SaveAs` | Documented troubleshooting topic ("Header Lost After SaveAs") — see the docs troubleshooting page for the current guidance for the installed version. |
| Mail merge leaves `«FieldName»`-style placeholders | The document has no template field with that exact name — check `doc.MailMerge.GetFieldNames()` first, and note `CaseInsensitiveFieldNames` if casing might differ. |
| `ToPdf` throws or is unavailable | It's an experimental method that needs `IronPdf` referenced. Use `IronPdf.DocxToPdfRenderer` directly instead (see ironpdf skill). |

## Rules

- **Never invent a member.** Confirm against the XML documentation that ships in the package
  before writing code:
  `grep -o 'name="[MPF]:IronWord\.[^"]*Table[^"]*"' ~/.nuget/packages/ironword/<version>/lib/netstandard2.0/IronWord.xml`
  That file (and the compiled `IronWord.dll` via reflection, which is more authoritative when
  the two disagree) is the real surface for the installed version — some methods described in
  marketing/blog pages (e.g. Markdown import/export) were **not** present in the shipped
  2026.8.1 assembly when checked; do not rely on them without re-verifying against the exact
  installed version.
- Keep licence keys out of source and out of terminal output.
- Treat a missing/invalid licence as a blocker: `SaveAs` throws rather than degrading quietly.
- Style text via `TextStyle` (on `TextContent.Style` or `Run.Style`), never via the read-only
  convenience properties on `TextContent` directly.
- `AddSection`, and the collection-add methods generally, mostly return `void` — read newly
  added items back from the owning collection (`doc.Sections`, `doc.Paragraphs`, etc.) rather
  than assuming a fluent/returning API.
- Official docs and full API reference: <https://ironsoftware.com/csharp/word/docs/>. Support:
  support@ironsoftware.com.
