# Headings

> Headings 1–4, toggleable headings and the table of contents.

Each example below is a real Notion block, rendered by notionic's built-in components, followed by a recipe to change it. The example headings also appear in this page's outline.

# Headings 1–4

**In Notion:** type `/h1`, `/h2`, `/h3` or `/h4`, or start a line with `#`, `##`, `###` or `####`.

# A heading 1

## A heading 2

### A heading 3

#### A heading 4

**Renders as:** `<h2>` to `<h5>`, one level down, so the page title stays the only `<h1>`. Each gets an anchor id from its text. See [Supported blocks](https://notionic.dev/docs/supported-blocks).

```typescript
// Customize · heading.tsx
import { RichText, type BlockProps } from "notionic"

// Keep the anchor id from context.headings, and add a link to it.
export function Heading2({ block, context }: BlockProps<"heading_2">) {
  const id = context.headings.find((heading) => heading.blockId === block.id)?.id
  return (
    <h2 id={id} className="group scroll-mt-20 text-2xl font-semibold">
      <RichText text={block.heading_2.rich_text} context={context} />
      {id ? (
        <a href={`#${id}`} className="ms-2 text-muted-foreground opacity-0 group-hover:opacity-100">
          #
        </a>
      ) : null}
    </h2>
  )
}

<NotionRenderer blocks={blocks} components={{ heading_2: Heading2 }} />
```

# Toggleable headings

**In Notion:** type `/toggle heading`, or turn a heading into a toggle from its `⋮⋮` menu.

### A toggleable heading

Its content stays in the HTML while closed, so search engines and Cmd+F find it.

**Renders as:** a `<details>` whose summary is the heading. It opens without JavaScript.

```typescript
// Customize · toggle.tsx
import { NotionToggle } from "@/components/notion-toggle"

// shadcn/ui's Collapsible, from the registry, for toggles. Toggleable headings keep the built-in <details>.
<NotionRenderer blocks={blocks} components={{ toggle: NotionToggle }} />
```

# Table of contents

**In Notion:** type `/table of contents`.

**Renders as:** a list of links to every heading on the page, indented by level.

```typescript
// Customize · outline.tsx
import { collectHeadings, type NotionBlock } from "notionic"

// The same headings, as a sidebar outline outside the page.
export function Outline({ blocks }: { blocks: NotionBlock[] }) {
  return (
    <nav aria-label="On this page" className="flex flex-col gap-1 text-sm">
      {collectHeadings(blocks).map((heading) => (
        <a key={heading.id} href={`#${heading.id}`} style={{ paddingInlineStart: (heading.level - 1) * 12 }}>
          {heading.text}
        </a>
      ))}
    </nav>
  )
}
```
