---
title: "Create a side-by-side HTML diff in C#"
url: https://recursiveai.org/onediff/cookbook/side-by-side-html-diff/
description: "Compare two text documents in C# and save a standalone side-by-side HTML report with OneDiff. Includes inline inputs, output, and an inline-layout variation."
version: "1.0.1"
---

# Create a side-by-side HTML diff in C#

**OneDiff** is a free, open-source diff library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneDiff.Documents) · [GitHub](https://github.com/recursive-ai-oss/onediff-csharp) · [Docs](https://recursiveai.org/onediff/docs/) · [Download example (.zip)](https://recursiveai.org/onediff/cookbook/side-by-side-html-diff/side-by-side-html-diff.zip)

Show plain-text changes in two HTML columns, not rendered web pages.

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

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

```csharp
using OneDiff.Documents;
using OneDiff.Documents.Model;

var oldDoc = CompareDocument.FromText(
    "Pay in 30 days.");
var newDoc = CompareDocument.FromText(
    "Pay in 60 days.");
Comparison.Of(oldDoc, newDoc).SaveAs(
    "diff.html", RedlineForm.HtmlSideBySide);
Console.WriteLine("Saved diff.html");
```

```text
Saved diff.html
```

Unzip [the example](https://recursiveai.org/onediff/cookbook/side-by-side-html-diff/side-by-side-html-diff.zip), then `dotnet run`.

## The full program

The full program adds the labels `old` and `new` to identify the report’s columns; both inputs are literal sentences, so there are no sample files to fetch. Open `diff.html` in a browser after running. File-writing errors propagate to the caller; choose a writable output directory.

```csharp
using OneDiff.Documents;
using OneDiff.Documents.Model;

var oldDoc = CompareDocument.FromText(
    "Pay within 30 days.", "old");
var newDoc = CompareDocument.FromText(
    "Pay within 60 days.", "new");
var comparison = Comparison.Of(oldDoc, newDoc);
comparison.SaveAs("diff.html",
    RedlineForm.HtmlSideBySide);
Console.WriteLine("Saved diff.html");
```

`dotnet run` prints:

```text
Saved diff.html
```

The full program’s `diff.html` contains this two-column table (excerpt; the report’s stylesheet is not shown):

```html
<table class="sbs">
<caption class="sr">Old and new text side by side</caption>
<thead><tr><th scope="col">Old: old</th><th scope="col">New: new</th></tr></thead>
<tbody>
<tr><td><div class="blk k-paragraph d1 chg" data-old="1" data-link="1"><p class="t">Pay within <del data-change="0"><span class="gl" aria-hidden="true">−</span><span class="sr"> deleted: </span>3<span class="sr"> end deleted </span></del>0 days.</p></div></td><td><div class="blk k-paragraph d1 chg" data-new="1" data-old="1" data-link="1"><p class="t">Pay within <ins data-change="0"><span class="gl" aria-hidden="true">+</span><span class="sr"> inserted: </span>6<span class="sr"> end inserted </span></ins>0 days.</p></div></td></tr>
</tbody>
</table>
```

The left cell marks the deleted `3` in `30`; the right marks the inserted `6` in `60`.

## Variations

**Use the same comparison with an inline layout.** This complete program writes a single flow with additions and deletions instead of two columns.

```csharp
using OneDiff.Documents;
using OneDiff.Documents.Model;

var oldDoc = CompareDocument.FromText(
    "Pay in 30 days.");
var newDoc = CompareDocument.FromText(
    "Pay in 60 days.");
Comparison.Of(oldDoc, newDoc).SaveAs(
    "inline.html", RedlineForm.Html);
Console.WriteLine("Saved inline.html");
```

```text
Saved inline.html
```

## Related recipes

- [Compare two strings word by word](https://recursiveai.org/onediff/cookbook/compare-text/)
- [Apply a unified diff patch](https://recursiveai.org/onediff/cookbook/apply-unified-patch/)

## The short answer

Use the `RecursiveAI.OneDiff.Documents` package, construct two text documents with `CompareDocument.FromText`, and save their comparison with `RedlineForm.HtmlSideBySide`. The output is a self-contained HTML file. The full example labels the inputs `old` and `new`, so the report’s column headers identify which side you are reading. No PDF, OCR or Word adapter is involved.

## How do I create a side-by-side HTML diff in C#?

Start with the original and revised text in the order shown in the snippet at the top. Reversing them reverses the meaning of additions and deletions. `Comparison.Of` builds the comparison; `SaveAs` selects the report form. Specify `HtmlSideBySide` explicitly rather than relying on the `.html` extension to choose the layout you want.

The [HTML output documentation](https://recursiveai.org/onediff/docs/output-html/) describes the table’s rows: unchanged blocks appear on both sides, deletions on the left, and insertions on the right. A moved block can appear at both its old and new places. A split or merge groups its blocks into one row. This is a document comparison view, not a promise that every visual row corresponds to one physical line of a source file.

The side-by-side form selects its preferred style when the comparison settings still have the default blackline style: blue double-underlined additions and red struck-through deletions. Its marks use blue and orange highlights plus glyphs such as plus and minus; the report does not rely on color alone for those marks. If a single reading flow is better for your reviewers, the variation uses `RedlineForm.Html` on the same kind of comparison. That changes the report layout rather than the inputs.

The generated page contains inline CSS, system font stacks and no scripts or external fetches. You can move the one HTML file to another folder and open it without copying a stylesheet directory. The output has no timestamp unless a timestamp is explicitly supplied through the style.

## Can I compare two HTML files and show the differences?

This recipe writes HTML as its **output**. It does not load a web page, execute JavaScript, render CSS, or compare browser screenshots. Its inputs are plain-text documents. If you put HTML source into `FromText`, tags are compared as text, not interpreted as a browser document tree. To compare the wording in two `.html` files, extract their text with an HTML parser first, then pass each extracted string to `CompareDocument.FromText`.

Document text and names are escaped by the report writer. Ampersands, less-than signs and greater-than signs become entities rather than active markup. Characters HTML cannot carry use visible control-character representations. Attribute values have their own escaping rules, and style colors are checked before entering the stylesheet. These rules are documented in the output writer’s escaping section; they are not a browser-based HTML comparison feature.

Finally, the report displays the text under the comparison settings. The default Legal settings focus on substantive document changes: typography and whitespace differences are counted in “Differences not shown” rather than drawn as edits. Hidden categories are not necessarily absent from the source. Read that ledger when a visible difference appears to be missing, and choose settings based on what reviewers must see.
