# Sidebar

You write the sidebar yourself: walk the document tree and render each item with the `Sidebar` components. The structure, order and look of every item are in your code.

## A complete example

```astro
---
import { Sidebar } from '@minidocs/astro/components';
import { navState } from '@minidocs/astro/runtime';
import { tree } from '../lib/docs';

const { id } = Astro.props;
const nav = navState(tree, id);
---

<Sidebar.Root id="sidebar" label="Docs">
  <Sidebar.List>
    {tree.map(function item(node) {
      return (
        <Sidebar.Item>
          {node.children.length > 0 ? (
            <Sidebar.Group {...nav.of(node)}>
              <Fragment slot="label">{node.label}</Fragment>
              <Sidebar.List>{node.children.map(item)}</Sidebar.List>
            </Sidebar.Group>
          ) : (
            <Sidebar.Link {...nav.of(node)}>{node.label}</Sidebar.Link>
          )}
        </Sidebar.Item>
      );
    })}
  </Sidebar.List>
</Sidebar.Root>
```

The callback of `tree.map` is named `item`, so a group with children renders the next level with `node.children.map(item)`, and a tree of any depth renders. On multilingual sites, use `tree[locale]` instead of `tree`.

## `nav.of(node)`

`navState(tree, id)` records where the current page sits in the tree, and `nav.of(node)` returns what rendering a node needs:

| Field     | Meaning                                             |
| --------- | --------------------------------------------------- |
| `href`    | The URL of a page or external link, none for groups |
| `current` | Whether this node is the current page               |
| `open`    | Whether the current page is this node or below it   |

The fields share their names with the components’ props, so they can be spread straight in. You can also override any of them, such as keeping a group always open:

```astro
<Sidebar.Group {...nav.of(node)} open>
```

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

## Kinds of groups

### Labels

Groups on the first level are often just a small heading with pages listed below, without collapsing. Use `Sidebar.Label` with `Sidebar.List`. This renders labels on the first level and collapsible groups deeper down:

```astro
{tree.map(function item(node) {
  return (
    <Sidebar.Item>
      {node.children.length === 0 ? (
        <Sidebar.Link {...nav.of(node)}>{node.label}</Sidebar.Link>
      ) : tree.includes(node) ? (
        <>
          <Sidebar.Label>{node.label}</Sidebar.Label>
          <Sidebar.List>{node.children.map(item)}</Sidebar.List>
        </>
      ) : (
        <Sidebar.Group {...nav.of(node)}>
          <Fragment slot="label">{node.label}</Fragment>
          <Sidebar.List>{node.children.map(item)}</Sidebar.List>
        </Sidebar.Group>
      )}
    </Sidebar.Item>
  );
})}
```

### Collapsible groups

`Sidebar.Group` renders a group that collapses and expands when its title is clicked, as in the complete example above. Its title goes in the `label` slot.

### Accordions

Give groups on the same level the same `name`, and opening one closes the others. Groups with a page of their own are not included:

```astro
<Sidebar.Group {...nav.of(node)} name="sections">
```

### Groups with their own page

A group defined with `page(id, children)` is also a page, and `nav.of(node)` gives it an `href`. Its title behaves differently depending on the current page:

| Current page            | Clicking the title                      |
| ----------------------- | --------------------------------------- |
| Another page            | Goes to the group’s page and expands it |
| A page inside the group | Goes to the group’s page                |
| The group’s own page    | Collapses or expands it                 |

## Keeping state across pages

Going from one page to another, the sidebar stays as it was: open groups and the scroll position do not change. Groups containing the current page are always open, so you can always see where you are.

## The menu on narrow screens

`Sidebar.Trigger` is the button that opens and closes the sidebar, with `target` set to the sidebar’s `id`:

```astro
<Sidebar.Trigger target="sidebar" aria-label="Menu">☰</Sidebar.Trigger>
<Sidebar.Root id="sidebar" label="Docs">...</Sidebar.Root>
```

Inside `Layout.Sidebar`, the default styles turn the sidebar into a drawer that slides in from the left on narrow screens, closing on Esc or a click outside, see [Page Layout](/en/docs/guides/layout.md).
