---
title: "Hidden lines"
description: "Hide the imports and set-up that readers need to run an example but not to understand it."
url: "https://ewels.github.io/starlight-codeblocks/features/hidden-lines/"
markdown: "https://ewels.github.io/starlight-codeblocks/features/hidden-lines.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"
---

# Hidden lines

> Hide the imports and set-up that readers need to run an example but not to understand it.

A working example often needs lines that the reader can skip, such as imports, settings or the boilerplate around the part you explain. The `hidden` attribute and `[!code hide]` remove these lines from view, behind a marker the reader can open.

````md
```py title="summary.py" hidden={1-3,6-7}
import json
from pathlib import Path

config = json.loads(Path("config.json").read_text())
for key, value in config.items():
    if key.startswith("_"):
        continue
    print(f"{key} = {value}")
```
````

The imports and the `if` block are behind their own markers. Click a marker to show that run, or the title bar button to show every hidden line at once. Copying the block gives the whole file, imports included.

## Syntax

| Syntax | Where |
|---|---|
| `hidden={range}` | Code block fence line |
| `[!code hide]` | Comment |
| `[!code hide:N]` | Comment |

`hidden={range}` takes a [range](https://ewels.github.io/starlight-codeblocks/comment-notation/#line-numbers), such as `{2}` or `{1, 4-6}`. `[!code hide:N]` hides its own line and the next N-1 lines. You can use the attribute and the directive in the same block.

## Examples

### Hide lines with a directive

A directive keeps the hidden line next to the code it belongs to, so it moves if you reorder the block:

````md
```ts title="server.ts"
import { createServer } from 'node:http'; // [!code hide]
import { readFileSync } from 'node:fs'; // [!code hide]

const port = Number(process.env.PORT ?? 3000);
createServer((req, res) => res.end(readFileSync('index.html'))).listen(port);
```
````

### A block without a title

A block with no title still gets a title bar, which holds only the toggle button:

````md
```js hidden={1-2}
import { readFile } from 'node:fs/promises';

const notes = await readFile('notes.txt', 'utf8');
console.log(notes.length);
```
````

## Behaviour

- Markers:
  - Each run of hidden lines is replaced by a dashed marker, with the text "N hidden lines".
  - Clicking a marker shows that run only. Its text changes to "Hide N lines". The lines get a faint background, and their code is dimmed to 75% opacity, so the reader can tell they asked for them.
  - The dashed rule of a marker gets brighter when the mouse cursor is on it, and fainter while its lines show.
- The title bar button:
  - The title bar has a button, "Show N hidden lines", where N is the total across every run. It shows every run at once. Once every run is open, it reads "Hide N lines".
- Screen readers:
  - Each marker has `aria-expanded`. The label of the title bar button changes from **Show** to **Hide**. Both have `aria-controls`, so assistive technology reports what they open.
- Copy and print:
  - The copy button always includes hidden lines, so the code still runs after a paste. A manual selection includes only the hidden lines that are open, and never the text of a marker.
  - Hidden lines stay hidden when the page prints. The markers and the title bar button do not print.
- Other features:
  - An [inline callout](https://ewels.github.io/starlight-codeblocks/features/inline-callouts/) on a hidden line shows only while the line is open.
- Without JavaScript:
  - Hidden lines stay hidden, and the markers and the title bar button do nothing.

## Options

Hidden lines have no options. Turn the feature off for the whole site with `hiddenLines: false`:

```js title="astro.config.mjs"
codeblocks({
  hiddenLines: false,
});
```

With `hiddenLines: false`, the plugin ignores `hidden={range}`, and `[!code hide]` stays in the code as written.

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

## Hidden lines or collapsible sections

Expressive Code has its own plugin for a similar job, [`@expressive-code/plugin-collapsible-sections`](https://expressive-code.com/plugins/collapsible-sections/). Its `collapse={range}` attribute folds lines into a section with a summary line, such as "3 collapsed lines". The plugin is not part of Starlight, so a site must install it and add it to the `plugins` list of its `ec.config.mjs` file. That list replaces the plugins of Starlight, so add `pluginCodeblocks()` to it too, as in [Sites with an ec.config.mjs file](https://ewels.github.io/starlight-codeblocks/getting-started/#sites-with-an-ecconfigmjs-file):

```js title="ec.config.mjs"
import { pluginCollapsibleSections } from '@expressive-code/plugin-collapsible-sections';
import { pluginCodeblocks } from 'starlight-codeblocks/expressive-code';

export default {
  plugins: [pluginCollapsibleSections(), pluginCodeblocks()],
};
```

This site uses the plugin, so the examples below are live. Here is the first example of this page again, with `collapse` in place of `hidden`:

````md
```py title="summary.py" collapse={1-3,6-7}
import json
from pathlib import Path

config = json.loads(Path("config.json").read_text())
for key, value in config.items():
    if key.startswith("_"):
        continue
    print(f"{key} = {value}")
```
````

Each section keeps a summary line in the block, where hidden lines leave only a thin marker. Click a summary line to open its section.

With `collapseStyle`, an open section keeps a line to close it again. `collapsible-auto` puts that line at the start of the section, or at the end if the section ends the block:

````md
```py title="summary.py" collapse={6-7} collapseStyle="collapsible-auto"
import json
from pathlib import Path

config = json.loads(Path("config.json").read_text())
for key, value in config.items():
    if key.startswith("_"):
        continue
    print(f"{key} = {value}")
```
````

The two features compare as follows:

| Aspect | Hidden lines | Collapsible sections |
|---|---|---|
| Space before a reader opens it | A thin dashed marker | A full summary line for each section |
| Syntax | `hidden={range}`, `[!code hide]` and `[!code hide:N]` | `collapse={range}` |
| Show every section at once | A title bar button | No |
| Open lines look different | Yes: a faint background, and code at 75% opacity | Only with a `collapsible-*` style |
| Close a section again | The marker, or the title bar button | The summary line, with a `collapsible-*` style |
| Copy button | Copies every line | Copies every line |
| Without JavaScript | The lines stay hidden | Works: the sections are HTML `<details>` elements |
| Needs | This plugin | A separate Expressive Code plugin |

Use hidden lines for lines that most readers never need, such as imports, and when the block must stay compact. The directive keeps the hidden state next to its line, so it moves when you edit the block. Use collapsible sections when readers often open the lines, or when the page must work without JavaScript. Both plugins work on one site, and each block can use either one.

## Limitations

- A hidden line still runs in the copied code. If a line depends on something the reader must change, such as an API key, keep it visible or use a [fill-in placeholder](https://ewels.github.io/starlight-codeblocks/features/fill-in-placeholders/) instead.

## Related

- [Focus](https://ewels.github.io/starlight-codeblocks/features/focus/): blur the lines around the ones you explain, and keep them in view.
- [Expandable blocks](https://ewels.github.io/starlight-codeblocks/features/expandable-blocks/): cap a long block at a line count, instead of a list of lines to hide.
- [Collapsible sections](https://expressive-code.com/plugins/collapsible-sections/): the Expressive Code plugin that folds lines behind a summary line.
- [Comment notation](https://ewels.github.io/starlight-codeblocks/comment-notation/): the directive syntax that `[!code hide]` uses.
