---
title: "Word-level diff"
description: "Highlight the words that changed inside each line of a diff, so readers find a small edit in a long line."
url: "https://ewels.github.io/starlight-codeblocks/features/word-level-diff/"
markdown: "https://ewels.github.io/starlight-codeblocks/features/word-level-diff.md"
section: "Make code easier to read"
site: "starlight-codeblocks documentation"
context: "This page is from the documentation of starlight-codeblocks. A Starlight plugin that adds focus, line states, annotations, API auto-linking and 22 more features to the code blocks of a site."
index: "https://ewels.github.io/starlight-codeblocks/llms.txt"
---

# Word-level diff

> Highlight the words that changed inside each line of a diff, so readers find a small edit in a long line.

Expressive Code tints the added and removed lines of a `diff` block. Word-level diff also highlights the words that changed inside each pair of lines, so readers can find a one-word edit in a long line.

````md
```diff lang="js"
-const timeout = 5000;
+const timeout = options.timeout ?? 5000;
const client = createClient({
-  retries: 3,
+  retries: options.retries,
});
```
````

The new `timeout` line adds `options.timeout ??`, and `3` becomes `options.retries`. The plugin pairs each removed line with the added line below it, and compares them word by word. The changed words get a stronger tint and a bar under them. The rest of the line keeps the plain diff tint.

## Syntax

| Syntax | Where |
|---|---|
| A `diff` block | Code block fence line |
| `ins={range}`, `del={range}` | Code block fence line |
| `[!code ++]`, `[!code --]` | Comment |
| `wordDiff=false` | Code block fence line |
| `wordDiff.minSimilarity=<0-1>` | Code block fence line |

Word-level diff applies to every removed line that has an added line below it, from any of these sources. You do not turn it on. `wordDiff=false` turns it off for one block.

## Examples

### With `ins` and `del`

The `ins` and `del` attributes give the same result as a `diff` block, without changing the language of the block:

````md
```js title="server.js" ins={2} del={1}
const port = 3000;
const port = Number(process.env.PORT ?? 3000);
```
````

### With directives

`[!code --]` and `[!code ++]` move with their line when you edit the code:

````md
```py title="config.py"
TIMEOUT = 30  # [!code --]
TIMEOUT = 60  # [!code ++]
```
````

### Lines too different to compare

A rewritten line is too different to compare, so it keeps a plain diff tint:

````md
```diff lang="js"
-const total = a + b;
+export function computeGrandTotal(items) {
+  return items.reduce((sum, item) => sum + item.price, 0);
+}
```
````

## Behaviour

- Pairing lines:
  - The plugin pairs a run of removed lines with the run of added lines below it, one by one.
  - Each pair is compared token by token: words, runs of whitespace and single punctuation characters.
  - If a pair is less than 40% similar, it is too different to compare. The plugin leaves the whole-line tints in place and adds no word highlight.
- Highlights:
  - The changed tokens get a stronger tint and a bar in the diff colour. Whitespace between two changed tokens is part of the highlight, so a run of changed words tints as one block.
  - Changed words have a bar under them, and removed words also have a line through them, so the change does not rely on colour.
  - The plugin corrects the colour of each changed word to 4.5:1 contrast on the tint. In dark themes, this can turn the syntax colours of the words close to plain text.
- Without JavaScript:
  - Word-level diff needs no JavaScript in the browser.

## Options

### `wordDiff`

Highlights the words that changed between a removed line and an added line. Set it to `false` to turn the feature off.

- Type: `false | object`
- Default: On

### `wordDiff.minSimilarity`

Pairs of lines less similar than this keep whole-line tints only. From 0 to 1. A code block can set its own on its fence line.

- Type: `number`
- Default: `0.4`

Some codebases rewrite lines often enough that the word-level highlight adds noise more often than it helps. Raise the threshold to show it only for close edits:

```js title="astro.config.mjs"
codeblocks({
  wordDiff: { minSimilarity: 0.7 },
});
```

- [Configuration](https://ewels.github.io/starlight-codeblocks/configuration/): Learn how to set options for the whole site, or for a single code block.

## Limitations

- Pairing is by position. The first removed line pairs with the first added line, the second with the second, and each later pair the same way. A block that adds or removes a different number of lines can pair the wrong lines.
- The comparison works on the visible text of the line. It does not parse the language, so it cannot tell that `total` and `sum` are the same value with a new name.
- The plugin does not compare a long pair of lines, such as two lines of minified code. If the number of tokens in the removed line multiplied by the number in the added line is more than 1,000,000, the whole-line tints stay.

## Related

- [Line states](https://ewels.github.io/starlight-codeblocks/features/line-states/): tint a line as an error, a warning or a note, instead of a change.
- [Comment notation](https://ewels.github.io/starlight-codeblocks/comment-notation/): the `[!code ++]` and `[!code --]` directives that word-level diff reads.
