# Getting Started

This page starts from an Astro project and builds a docs site with a sidebar, a table of contents and previous and next links.

## Prerequisites

- Node.js 22.22 or later
- An Astro 7 project

## Install

```sh
pnpm add @minidocs/astro @astrojs/mdx
```

Add both integrations to `astro.config.mjs`. `@astrojs/mdx` lets you write docs in MDX, and `minidocs` handles links and callouts in your content:

```js
import mdx from '@astrojs/mdx';
import { defineConfig } from 'astro/config';
import minidocs from '@minidocs/astro';

export default defineConfig({
  integrations: [mdx(), minidocs()],
});
```

## Define a collection

Define the collection in `src/content.config.ts`, importing `glob` from `@minidocs/astro/loaders`. It is Astro’s `glob` with one more option, `prefix`, the path the pages live under:

```ts
import { defineCollection } from 'astro:content';
import { z } from 'astro/zod';
import { glob } from '@minidocs/astro/loaders';

const docs = defineCollection({
  loader: glob({ base: './src/content/docs', pattern: '**/*.{md,mdx}', prefix: 'docs' }),
  schema: z.object({
    title: z.string(),
  }),
});

export const collections = { docs };
```

minidocs only needs a `title` in the schema, used as the label in the sidebar. Every other field is up to you.

## Write your first page

Create `index.mdx` and `hello.mdx` in `src/content/docs/`:

```mdx
---
title: Hello
---

This is the first page.
```

Every page has an id, which is its file path without the extension. These two pages are `index` and `hello`, at `/docs/` and `/docs/hello/`. See [Pages and URLs](/en/docs/concepts/pages-and-urls.md) for how ids and URLs are made.

## Define the tree

Pages in a collection have no order. `defineTree` arranges them into a tree, which the sidebar and the previous and next links follow:

```ts
// src/lib/docs.ts
import { getCollection } from 'astro:content';
import { defineTree, listPages } from '@minidocs/astro/runtime';

export const entries = await getCollection('docs');

export const tree = await defineTree(entries, ({ page, dir }) => [page('index'), ...dir('')]);

export const pages = listPages(entries);
```

`dir('')` lists the pages in the root directory except `index`. `listPages` lists every page to generate, which the route in the next step uses. Arguments of `page()` and `dir()` are autocompleted, and a wrong id is a type error. See [The Document Tree](/en/docs/concepts/document-tree.md) for more.

## Render pages

Put one route in the folder `prefix` names to render every page of the collection. Each page in `pages` carries its route parameters as `page.params`, ready for `getStaticPaths`. The `headings` from `render()` are the headings of the page, for the table of contents:

```astro
---
// src/pages/docs/[...slug].astro
import { render } from 'astro:content';
import Docs from '../../layouts/Docs.astro';
import { pages } from '../../lib/docs';

export async function getStaticPaths() {
  return pages.map((page) => ({ params: page.params, props: page }));
}

const { Content, headings } = await render(Astro.props.entry);
---

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

## Build the page

Last comes the page layout. `Layout`, `Sidebar`, `Toc`, `Pager` and `Prose` are minidocs components, and importing `@minidocs/astro/styles.css` gives them the full default look. The top of the layout computes the data the components need:

- `navState(tree, id)` records where the current page sits in the tree, and `nav.of(node)` gives each item its link and whether it is the current page
- `neighbors(tree, id)` finds the previous and next pages
- `tocTree(headings)` arranges the headings of the page into a table of contents

```astro
---
// src/layouts/Docs.astro
import { Layout, Pager, Prose, Sidebar, Toc } from '@minidocs/astro/components';
import { navState, neighbors, tocTree, toHref } from '@minidocs/astro/runtime';
import '@minidocs/astro/styles.css';
import { tree } from '../lib/docs';

const { id, entry, headings } = Astro.props;
const nav = navState(tree, id);
const toc = tocTree(headings);
const { prev, next } = neighbors(tree, id);
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{entry.data.title}</title>
  </head>
  <body>
    <Layout.Root>
      <Layout.Header>
        <Layout.Title href={toHref('docs', 'index')}>My docs</Layout.Title>
      </Layout.Header>

      <Sidebar.Trigger target="sidebar" aria-label="Menu">
        <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
          <path d="M2 4h12M2 8h12M2 12h12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
        </svg>
      </Sidebar.Trigger>

      <Layout.Sidebar>
        <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>
      </Layout.Sidebar>

      <Layout.Main>
        <Prose>
          <h1>{entry.data.title}</h1>
          <slot />
        </Prose>
        {(prev || next) && (
          <Pager.Root label="Pagination">
            {prev && (
              <Pager.Link rel="prev" href={nav.of(prev).href}>
                <Pager.Label>Previous</Pager.Label>
                <Pager.Title>{prev.label}</Pager.Title>
              </Pager.Link>
            )}
            {next && (
              <Pager.Link rel="next" href={nav.of(next).href}>
                <Pager.Label>Next</Pager.Label>
                <Pager.Title>{next.label}</Pager.Title>
              </Pager.Link>
            )}
          </Pager.Root>
        )}
      </Layout.Main>

      {toc.length > 0 && (
        <Layout.Aside title="On this page">
          <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>
        </Layout.Aside>
      )}
    </Layout.Root>
  </body>
</html>
```

Run `pnpm dev` and open `/docs/` to see a complete docs site: the sidebar on the left, the content in the middle, the table of contents on the right, previous and next links at the bottom, and a drawer for the sidebar on narrow screens.

This layout file is your code, so its structure, order and looks are all yours to change. Next:

- [Page Layout](/en/docs/guides/layout.md): what each layout component does, and how the page looks at different widths
- [Sidebar](/en/docs/guides/sidebar.md): the kinds of groups
- [Styling](/en/docs/guides/styling.md): colors, using part of the default styles, and Tailwind
