# Table of Contents

The table of contents lists the headings of the current page and highlights the section being read as the reader scrolls. As with the sidebar, you arrange the headings into a tree and render it with the `Toc` components.

## Getting the headings

Astro’s `render()` returns the headings of the page. Pass them to the layout in the route file:

```astro
---
// src/pages/docs/[...slug].astro
const { Content, headings } = await render(Astro.props.entry);
---

<Docs {...Astro.props} headings={headings}>
  <Content />
</Docs>
```

`headings` is a flat array, such as `[{ depth: 2, slug: 'install', text: 'Install' }, ...]`.

## Arranging them into a tree

`tocTree` turns the flat list into a tree, with each h3 under the h2 before it:

```ts
import { tocTree } from '@minidocs/astro/runtime';

const toc = tocTree(headings);
```

Only h2 and h3 are kept by default. To include h4, pass the range of levels: `tocTree(headings, { depth: [2, 4] })`.

## Rendering

```astro
---
import { Toc } from '@minidocs/astro/components';
---

<Toc.Root label="On this page">
  <Toc.List>
    {toc.map(function item(heading) {
      return (
        <Toc.Item>
          <Toc.Link slug={heading.slug}>{heading.text}</Toc.Link>
          {heading.children.length > 0 && <Toc.List>{heading.children.map(item)}</Toc.List>}
        </Toc.Item>
      );
    })}
  </Toc.List>
</Toc.Root>
```

See [Components and Styles](/en/docs/reference/components.md#table-of-contents-components) for the props of each component.

Inside `Layout.Aside`, the table of contents folds into a button at medium widths, see [Page Layout](/en/docs/guides/layout.md).

## Highlighting the current section

As the reader scrolls, the link of the section being read gets `aria-current="true"`, and the default styles place a marker beside it.

A heading becomes the current section once it scrolls past the reading line near the top of the page. The reading line is the heading’s `scroll-margin-top`, which is also where a heading stops after a jump from the table of contents, so jumping and highlighting always agree. With the `Layout` components, the default styles already set it. If you write your own layout with a header fixed at the top, set it to the header’s height:

```css
h2,
h3 {
  scroll-margin-top: 4rem;
}
```

At the bottom of the page, the last heading is highlighted even if it never reached the reading line.

## Highlighting parent headings

Only the link of the current section carries `aria-current`. To style its parent headings too, select the `<li>` that contains the current item with `:has()`:

```css
.md-toc-item:has([aria-current]) > .md-toc-link {
  font-weight: 600;
}
```
