Skip to content

Text & Line Markers

Expressive Code allows you to add annotations to lines & line ranges, as well as individual text inside your lines. You can use them to highlight important parts of your code, or to indicate changes between different versions of your code.

Good to know: No matter how much color you add to your code snippets, Expressive Code automatically ensures accessible color contrast, tweaking the text colors if necessary while keeping syntax highlighting intact.

Usage in markdown / MDX

Marking full lines & line ranges

Lines can be marked by adding their line numbers inside curly brackets to a code block’s meta information. Line numbers start at 1, just like in VS Code and other popular editors.

You can either mark a single line, or a range of lines, and you can combine multiple line markers by separating them with commas:

  • Single line: {4}
  • Three separate lines: {4, 8, 12}
  • Range of lines defined by a start and end: {4-8}
  • Multiple selectors combined: {1, 4, 7-8}

Here’s an example combining multiple line number & line range selectors:

```js {1, 4, 7-8}
// Line 1 - targeted by line number
// Line 2
// Line 3
// Line 4 - targeted by line number
// Line 5
// Line 6
// Line 7 - targeted by range "7-8"
// Line 8 - targeted by range "7-8"
```

This will render as follows:

// Line 1 - targeted by line number
// Line 2
// Line 3
// Line 4 - targeted by line number
// Line 5
// Line 6
// Line 7 - targeted by range "7-8"
// Line 8 - targeted by range "7-8"

Selecting line marker types (mark, ins, del)

By default, all targeted lines will use the marker type mark, which is rendered in a neutral color that just highlights the line without adding any semantic meaning to it.

There are two other marker types available that add semantic meaning to your lines: ins (inserted) and del (deleted). These are rendered in green and red, respectively, and are commonly used to indicate changes to your code.

To specify the marker type for targeted lines, add it in front of their opening curly brace, followed by an equals sign. For example, ins={4} would mark line 4 as inserted, and del={7-12} would mark lines 7 to 12 as deleted.

Here’s an example combining all three marker types:

```js title="line-markers.js" del={2} ins={3-4} {6}
function demo() {
console.log('this line is marked as deleted')
// This line and the next one are marked as inserted
console.log('this is the second inserted line')
return 'this line uses the neutral default marker type'
}
```

This will render as follows:

line-markers.js
function demo() {
console.log('this line is marked as deleted')
// This line and the next one are marked as inserted
console.log('this is the second inserted line')
return 'this line uses the neutral default marker type'
}

Adding labels to line markers

You can add a text label to any line marker, which will be rendered as a colorful box in the first line of the marked line range. This allows you to reference specific parts of your code in the surrounding text.

To add any text as a label, enclose it in single or double quotes and add it directly after the opening curly brace, followed by a colon (:). For example, ins={"A":6-10} would mark lines 6 to 10 as inserted and add the label A to them.

Here’s an example:

```jsx {"1":5} del={"2":7-8} ins={"3":10-12}
// labeled-line-markers.jsx
<button
role="button"
{...props}
value={value}
className={buttonClassName}
disabled={disabled}
active={active}
>
{children &&
!active &&
(typeof children === 'string' ? <span>{children}</span> : children)}
</button>
```

This will render as follows:

labeled-line-markers.jsx
<button
role="button"
{...props}
value={value}
className={buttonClassName}
disabled={disabled}
active={active}
>
{children &&
!active &&
(typeof children === 'string' ? <span>{children}</span> : children)}
</button>
Adding long labels on their own lines

If you want to use labels that are too long to fit on the side, you can add them above the marked line range instead. To do so, add an empty line inside your code block where you want the label to appear, and target this empty line as the beginning of your line range. Example:

```jsx {"1. Provide the value prop here:":5-6} del={"2. Remove the disabled and active states:":8-10} ins={"3. Add this to render the children inside the button:":12-15}
// labeled-line-markers.jsx
<button
role="button"
{...props}
value={value}
className={buttonClassName}
disabled={disabled}
active={active}
>
{children &&
!active &&
(typeof children === 'string' ? <span>{children}</span> : children)}
</button>
```

This will render as follows:

labeled-line-markers.jsx
<button
role="button"
{...props}
value={value}
className={buttonClassName}
disabled={disabled}
active={active}
>
{children &&
!active &&
(typeof children === 'string' ? <span>{children}</span> : children)}
</button>

Using diff-like syntax

Instead of adding line numbers to the opening code fence as shown above, you can also use the diff language, which is supported on many platforms (e.g. GitHub). Set the language in the opening code fence to diff and add a + or - marker to the first column of any line:

```diff
+this line will be marked as inserted
-this line will be marked as deleted
this is a regular line
```

To make the raw contents in your markdown / MDX document more readable, you can add whitespace after the + or - marker (not before), and align unchanged lines with the changed ones. This additional whitespace will be automatically detected and removed from the rendered code block:

```diff
+ this line will be marked as inserted
- this line will be marked as deleted
this is a regular line
```

Both variants above will render as follows:

this line will be marked as inserted
this line will be marked as deleted
this is a regular line

To avoid unexpected modifications of actual diff files (which would make them unusable), this plugin will automatically detect diff content based on its common metadata lines. It will detect unified and context mode diff syntax like ***, +++, ---, @@, as well as the default mode location syntax (e.g. 0a1, 1,2c1,2, 1,2d1):

```diff
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+this is an actual diff file
-all contents will remain unmodified
no whitespace will be removed either
```

Combining syntax highlighting with diff-like syntax

Usually, a downside of using the diff language is that you lose syntax highlighting of the actual code’s language. To work around this, this plugin allows you to specify a second language identifier by adding a lang="..." attribute to the opening code fence. The value of this attribute will then be used for syntax highlighting, while the diff-like syntax can be used for marking lines:

```diff lang="js"
function thisIsJavaScript() {
// This entire block gets highlighted as JavaScript,
// and we can still add diff markers to it!
- console.log('Old code to be removed')
+ console.log('New and shiny code!')
}
```

This will render as follows:

function thisIsJavaScript() {
// This entire block gets highlighted as JavaScript,
// and we can still add diff markers to it!
console.log('Old code to be removed')
console.log('New and shiny code!')
}

Marking individual text inside lines

Plaintext search strings

To match a string of text inside your code block’s lines, simply wrap it in quotes. You can use either double or single quotes:

  • "this will be marked"
  • 'this will be marked'

If the text you want to match contains quotes itself, you can use the other quote type to wrap it without having to escape the nested quotes:

  • "these 'single' quotes need no escaping"
  • 'these "double" quotes need no escaping'

If you cannot avoid nested quotes of the same type, you can escape them using a backslash:

  • "this contains both \"double\" and 'single' quotes"
  • 'this contains both "double" and \'single\' quotes'

Example:

```js "given text"
function demo() {
// Mark any given text inside lines
return 'Multiple matches of the given text are supported';
}
```

This will render as follows:

function demo() {
// Mark any given text inside lines
return 'Multiple matches of the given text are supported';
}

Regular expressions

To match a regular expression inside your code block’s lines, wrap it in forward slashes:

```ts /ye[sp]/
console.log('The words yes and yep will be marked.')
```

This will render as follows:

console.log('The words yes and yep will be marked.')
Escaping forward slashes

To match a forward slash inside your regular expression, you can escape it using a backslash:

```sh /\/ho.*\//
echo "Test" > /home/test.txt
```

This will render as follows:

Terminal window
echo "Test" > /home/test.txt
Marking capture group contents

If you only want to mark certain parts matched by your regular expression, you can use capture groups. For example, the expression /ye(s|p)/ will match yes and yep, but only mark the character s or p:

The word "yes" will have the letter "s" marked.
This also works for the "p" in "yep".

To prevent this special treatment of capture groups, you can convert them to non-capturing groups by adding ?: after the opening parenthesis. For example:

This block uses `/ye(?:s|p)/`, which causes the full
matching words "yes" and "yep" to be marked.

Selecting inline marker types (mark, ins, del)

Just like with line markers, you can select the marker type for plaintext and regular expression markers by adding it in front of the opening quote or forward slash, followed by an equals sign. Here’s an example:

```js "return true;" ins="inserted" del="deleted"
function demo() {
console.log('These are inserted and deleted marker types');
// The return statement uses the default marker type
return true;
}
```

This will render as follows:

function demo() {
console.log('These are inserted and deleted marker types');
// The return statement uses the default marker type
return true;
}

Usage in the <Code> component

The text markers plugin adds multiple props to the <Code> component that allow direct access to its features. The following props are available:

Props

del

Defines the code block’s text & line markers of the “deleted” type.

You can either pass a single marker definition or an array of them.

ins

Defines the code block’s text & line markers of the “inserted” type.

You can either pass a single marker definition or an array of them.

mark

Defines the code block’s text & line markers of the default neutral type.

You can either pass a single marker definition or an array of them.

useDiffSyntax

Type: boolean

Allows you to enable processing of diff syntax for non-diff languages.

If set to true, you can prefix lines with + or -, no matter what the language of the code block is. The prefixes will be removed and the lines will be highlighted as inserted or deleted lines.

Referenced types

MarkerDefinition

Type: string | RegExp | number | { range: string; label?: string | undefined }

A single text marker definition that can be used in the mark, ins, and del props to define text and line markers.

Configuration

You can override this plugin’s default styles by adding a textMarkers object to the styleOverrides engine config option. You can find a list of all overridable styles below.

Here are configuration examples for common scenarios:

astro.config.mjs
import { defineConfig } from 'astro/config'
import astroExpressiveCode from 'astro-expressive-code'
export default defineConfig({
integrations: [
astroExpressiveCode({
styleOverrides: {
// You can optionally override the plugin's default styles here
textMarkers: {
// Make default marker color slightly purple
markHue: '310',
// Reduce marker border opacity
borderOpacity: '50%',
},
},
}),
],
})

Available plugin options

This plugin does not provide any configuration options that can be passed to its initialization function.

Available style overrides

This plugin adds a textMarkers object to the styleOverrides engine config option, allowing you to customize the visual appearance of the markers. The object contains the following properties:

backgroundOpacity

Type: UnresolvedStyleValue Default: '50%'

The opacity of the background color of all text marker types.

borderLuminance

Type: UnresolvedStyleValue Default: '48%'

The LCH luminance to be used for the border color of all text marker types.

borderOpacity

Type: UnresolvedStyleValue Default: '81.6%'

The opacity of the border color of all text marker types.

defaultChroma

Type: UnresolvedStyleValue Default: '40'

The LCH chroma to be used for all text marker types.

The chroma value defines the saturation of the color. Higher values lead to more saturated colors, lower values lead to less saturated colors.

defaultLuminance

Type: UnresolvedStyleValue Default: ['32%', '75%'] // 32% for dark themes, 75% for light themes

The LCH luminance to be used for all text marker types.

delBackground

Type: UnresolvedStyleValue Default: lch(<defaultLuminance> <defaultChroma> <delHue> / <backgroundOpacity>)

The background color of deleted text (text marker type del).

delBorderColor

Type: UnresolvedStyleValue Default: lch(<borderLuminance> <defaultChroma> <delHue> / <borderOpacity>)

The border color of deleted text (text marker type del).

delDiffIndicatorColor

Type: UnresolvedStyleValue Default: lch(<indicatorLuminance> <defaultChroma> <delHue> / <indicatorOpacity>)

The color of the diff indicator (e.g. + or -) of deleted lines.

delDiffIndicatorContent

Type: UnresolvedStyleValue Default: "'-'"

The content to be displayed inside the diff indicator of deleted lines.

Note that this is used as the content value in a CSS pseudo-element, so you need to wrap any text in additional quotes.

delHue

Type: UnresolvedStyleValue Default: '33' (a red hue)

The LCH hue to be used for deleted text (text marker type del).

indicatorLuminance

Type: UnresolvedStyleValue Default: ['67%', '40%'] // 67% for dark themes, 40% for light themes

The LCH luminance to be used for the diff indicator (e.g. + or -).

indicatorOpacity

Type: UnresolvedStyleValue Default: '81.6%'

The opacity of the diff indicator (e.g. + or -).

inlineMarkerBorderRadius

Type: UnresolvedStyleValue Default: '0.2rem'

The border radius of inline text markers.

inlineMarkerBorderWidth

Type: UnresolvedStyleValue Default: '1.5px'

The width of the border around inline text markers, rendered in a way that does not cause marked code to shift.

inlineMarkerPadding

Type: UnresolvedStyleValue Default: '0.15rem'

The inline padding of inline text markers. Keep this low to prevent marked code from shifting too much compared to the original text.

insBackground

Type: UnresolvedStyleValue Default: lch(<defaultLuminance> <defaultChroma> <insHue> / <backgroundOpacity>)

The background color of inserted text (text marker type ins).

insBorderColor

Type: UnresolvedStyleValue Default: lch(<borderLuminance> <defaultChroma> <insHue> / <borderOpacity>)

The border color of inserted text (text marker type ins).

insDiffIndicatorColor

Type: UnresolvedStyleValue Default: lch(<indicatorLuminance> <defaultChroma> <insHue> / <indicatorOpacity>)

The color of the diff indicator (e.g. + or -) of inserted lines.

insDiffIndicatorContent

Type: UnresolvedStyleValue Default: "'+'"

The content to be displayed inside the diff indicator of inserted lines.

Note that this is used as the content value in a CSS pseudo-element, so you need to wrap any text in additional quotes.

insHue

Type: UnresolvedStyleValue Default: '136' (a green hue)

The LCH hue to be used for inserted text (text marker type ins).

lineDiffIndicatorMarginLeft

Type: UnresolvedStyleValue Default: '0.3rem'

The margin between the code block border and the diff indicator (e.g. + or -) displayed on the left side of a full-line text marker.

lineMarkerAccentMargin

Type: UnresolvedStyleValue Default: '0rem'

The margin between the code block border and the line marker accent bar displayed on the left side of a full-line text marker.

lineMarkerAccentWidth

Type: UnresolvedStyleValue Default: '0.15rem'

The width of the line marker accent bar. This is the vertical border-like bar displayed on the left side of a full-line text marker.

lineMarkerLabelColor

Type: UnresolvedStyleValue Default: 'white'

The text color of the optional labels that can be displayed on the left side of a full-line text marker.

lineMarkerLabelPaddingInline

Type: UnresolvedStyleValue Default: '0.2rem'

The inline padding (= left & right padding in horizontal writing mode) around line marker labels.

markBackground

Type: UnresolvedStyleValue Default: lch(<defaultLuminance> <defaultChroma> <markHue> / <backgroundOpacity>)

The background color of marked text (text marker type mark).

markBorderColor

Type: UnresolvedStyleValue Default: lch(<borderLuminance> <defaultChroma> <markHue> / <borderOpacity>)

The border color of marked text (text marker type mark).

markHue

Type: UnresolvedStyleValue Default: '284' (a blue hue)

The LCH hue to be used for marked text (text marker type mark).