---
title: "Split a Word document by heading in C#"
url: https://recursiveai.org/onedocument/cookbook/split-word-by-heading/
description: "Heading 1 starts each output file."
version: "1.0.0"
---

Heading 1 starts each output file.

```shell
dotnet add package RecursiveAI.OneDocument.Documents
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using RecursiveAI.OneDocument.Documents;
using var doc = Document.Open("chapters.docx");
using var split = doc.SplitByHeading(1);
int number = 0;
foreach (var piece in split.Pieces)
    piece.Document.SaveAs($"{++number}.docx");
```

Writes `1.docx` and `2.docx`; the snippet prints nothing.

Unzip [the example](https://recursiveai.org/onedocument/cookbook/split-word-by-heading/split-word-by-heading.zip), then `dotnet run`.

## Full program

The included `chapters.docx` has this structure:

```text
Heading 1: Introduction
  Project overview.
  Heading 2: Details
    Schedule and costs.
Heading 1: Delivery
  Ship in June.
```

```csharp
using RecursiveAI.OneDocument.Documents;

using var doc = Document.Open("chapters.docx");
using var split = doc.SplitByHeading(1);
int number = 0;
foreach (var piece in split.Pieces)
{
    string path = $"{++number}.docx";
    piece.Document.SaveAs(path);
    Console.WriteLine($"{path}: {piece.Title}");
}
```

`dotnet run` prints:

```text
1.docx: Introduction
2.docx: Delivery
```

A heading level outside 1–9 throws `ArgumentOutOfRangeException`; a document without matching headings returns one piece.

## Variations

### Include subheadings as split points

Level 2 splits at Heading 1 and Heading 2, not only at Heading 2. This snippet lists the resulting titles without writing files.

```csharp
using RecursiveAI.OneDocument.Documents;

using var doc = Document.Open("chapters.docx");
using var split = doc.SplitByHeading(2);
foreach (var piece in split.Pieces)
    Console.WriteLine(piece.Title);
```

Output:

```text
Introduction
Details
Delivery
```

## Related recipes

- [Merge Word documents in C#](https://recursiveai.org/onedocument/cookbook/merge-word-documents/)
- [Read word document](https://recursiveai.org/onedocument/cookbook/read-word-document/)

## The short answer

Open the DOCX, call `SplitByHeading(1)`, then save each `DocumentPiece.Document`. Level 1 starts a new piece before each top-level heading. The source stays open separately, and the returned `SplitResult` owns the piece documents; dispose both `doc` and `split` after saving the pieces. The two `using var` declarations do this automatically.

The key distinction is structure, not appearance. A large bold paragraph is not necessarily a heading. OneDocument looks at outline levels and heading styles, so the document must identify its chapter titles as headings for this operation to find them.

## Which heading level should I pass?

Use the deepest heading level that should start a new output file. `SplitByHeading(1)` keeps a chapter’s subheadings within that chapter. `SplitByHeading(2)` splits at both Heading 1 and Heading 2. It does not mean “Heading 2 only”. Valid values run from 1 through 9.

A heading is a paragraph whose outline level, set directly or inherited from its style, or built-in heading style identifies it as such. This behavior is documented on [the split API](https://github.com/recursive-ai-oss/onedocument-csharp/blob/a85720e075c783fc596641d8586ee126cae088c5/src/OneDocument.Documents/Operations/DocumentSplit.cs#L78-L83). If your chapter titles are only visually formatted, assign heading styles in the source before splitting.

Text before the first matching heading becomes the first piece rather than being discarded. A document without matching headings produces one piece. Keep that case in mind when a job expected many chapters: one output can mean the input has no usable heading markup, not that the save failed.

`DocumentPiece.Title` gives the heading text. The example uses numbered filenames instead, so headings with punctuation or duplicate names do not become output paths. Each piece also reports `FirstBlock` and `BlockCount` in the original body. These are body-block positions, not page numbers or character offsets.

## How do I split a Word document into two separate documents?

Heading splitting gives two files when the document has two matching heading ranges, as the sample does. If you need a fixed boundary unrelated to headings, `SplitBefore(blockIndices)` starts pieces at selected body-block indexes. For section boundaries, `SplitBySections()` is the structural alternative. These are not equivalent to cutting at a rendered page boundary.

A split piece is made by saving the original in memory and removing the body blocks outside its range. The section covering the end of that range becomes the piece’s final section. Inherited header and footer references are made explicit where needed. Then unused styles, numbering, notes, comments, relationships and unreachable parts are pruned according to the [merge and split documentation](https://recursiveai.org/onedocument/docs/operations-merge/).

This matters when distributing chapters independently. A piece is a standalone document, not a text excerpt that depends on keeping the original file nearby. However, cross-references to material outside a piece may no longer have a target. Do not treat cutting the body as a guarantee that every reference still makes sense in isolation.

Page-based splitting is a separate operation in the rendering package. It needs layout information and can only start a piece at a body block. A paragraph or table that crosses a page boundary is not cut through its middle by the heading API. Choose heading splitting for chapters and sections of content; choose a layout-based operation only when actual pages are the requirement.
