---
title: "Adding collaboration to Pages"
description: "Learn how to add real-time collaboration to your paginated editor — including headers, footers, the four subtypes (default, first page, odd/even), and document-level page geometry like format and margins — using the Pages extension."
canonical_url: "https://tiptap.dev/docs/pages/guides/collaboration-with-pages"
---

# Adding collaboration to Pages

Learn how to add real-time collaboration to your paginated editor — including headers, footers, the four subtypes (default, first page, odd/even), and document-level page geometry like format and margins — using the Pages extension.

Pages supports real-time collaboration on the **main document body**, on **every header/footer sub-editor**, and on the **document-level page geometry** (page format, page gap, header/footer margin overrides) out of the box. Each header/footer subtype — default, first page, odd, even — gets its own Y field, so multiple users can edit them simultaneously and see remote changes propagate live into both the open editor and the page-level preview rendered on every page. Switching the page format or dragging a margin slider on one client updates the layout on every connected client.

> **Interactive demo:** [PagesCollaboration](https://embed-pro.tiptap.dev/preview/Extensions/PagesCollaboration)

---

## How it works

When `@tiptap/extension-collaboration` is wired on the parent editor, the Pages extension hands you the `Y.Doc` through a `headerFooterExtensions` callback so you can attach a `Collaboration` extension to each header/footer sub-editor. The package does not bundle `yjs` or `@tiptap/extension-collaboration` — your application owns those dependencies and attaches them through the callback.

Every header/footer subtype (default, first page, odd, even — for both headers and footers) collaborates independently. The configuration toggles `differentFirstPage` / `differentOddEven` and the document-level page geometry (`pageFormat`, `pageGap`, `headerTopMargin`, `footerBottomMargin`) all sync across clients automatically — toggling a flag or changing the format on one client flips it on the others within a frame. Remote edits propagate into the page-level preview on every client even when no overlay is open.

If you also wire `@tiptap/extension-collaboration-caret` (`CollaborationCursor` in Tiptap v2) on the parent editor, remote carets and user labels appear in the main document body. The callback context exposes the provider and local user via `ctx.cursor`, but see the caveat below — the recommended setup keeps awareness on the main editor only, not on the sub-editors.

---

## 1. Requirements

- A Tiptap Pro plan that supports collaboration
- Access to Tiptap Cloud or your own collaboration backend
- The Pages stack: `@tiptap-pro/extension-convert-kit`, `@tiptap-pro/extension-pages-tablekit`, `@tiptap-pro/extension-pages`
- A collaborative provider: `@tiptap-pro/provider` (or your own) plus `@tiptap/extension-collaboration` and `yjs`

## 2. Install the required packages

```bash
npm install @tiptap-pro/extension-convert-kit \
            @tiptap-pro/extension-pages-tablekit \
            @tiptap-pro/extension-pages \
            @tiptap-pro/provider \
            @tiptap/extension-collaboration \
            yjs
```

## 3. Set up your collaborative provider

```ts
import { TiptapCollabProvider } from '@tiptap-pro/provider'
import * as Y from 'yjs'

const doc = new Y.Doc()

const provider = new TiptapCollabProvider({
  name: 'document.name', // Unique document identifier for syncing
  appId: 'your-app-id', // From the Cloud dashboard, or use `baseURL` for on-premises
  token: 'your-jwt', // JWT generated by your server
  document: doc,
})
```

## 4. Configure your editor with the Pages stack and collaboration

Use the **callback form** of `headerFooterExtensions`. Pages will call your callback once per subtype (`default`, `first`, `odd`, `even` × `header`, `footer`) with a context describing whether collaboration is active and what Y field the sub-editor should bind to. Return the extensions for that subtype, including a `Collaboration` extension when `ctx.isCollaborative` is true.

```ts
import { Editor } from '@tiptap/core'
import Collaboration from '@tiptap/extension-collaboration'
import { ConvertKit } from '@tiptap-pro/extension-convert-kit'
import { TableKit } from '@tiptap-pro/extension-pages-tablekit'
import { Pages } from '@tiptap-pro/extension-pages'

const editor = new Editor({
  extensions: [
    ConvertKit.configure({ table: false }),
    TableKit,
    Pages.configure({
      pageFormat: 'A4',
      // ⚠️ Do NOT pass `header` / `footer` defaults here — see "What to expect"
      // below. With collaboration on, Y is the source of truth for header/footer
      // content; defaults compete with that and can re-appear after a remote clear.
      headerFooterExtensions: ctx => {
        // Mirror the main editor's stack inside each sub-editor so the schema,
        // marks, and tables behave identically there.
        const base = [ConvertKit.configure({ table: false }), TableKit]

        if (!ctx.isCollaborative || !ctx.ydoc) {
          // No collaboration on the parent editor — return your base stack.
          return base
        }

        return [
          ...base,
          Collaboration.configure({
            document: ctx.ydoc,
            field: ctx.field,
          }),
        ]
      },
    }),
    Collaboration.configure({
      document: doc,
    }),
  ],
})
```

> **One callback, eight sub-editors:**
>
> Your callback is called once per `(editorType, subType)` pair — eight times in total. Each
> invocation receives a distinct `ctx.field` so the matching sub-editor stays in sync with its own
> slice of the document. Subtypes that aren't currently being edited still receive remote updates
> in the background, so the page-level preview reflects changes immediately on every client.

### Adding awareness with `CollaborationCaret`

Install `@tiptap/extension-collaboration-caret` (`@tiptap/extension-collaboration-cursor` in Tiptap v2) and attach it to the **main editor**. Remote carets and user labels then appear in the main document body:

```ts
import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCaret from '@tiptap/extension-collaboration-caret'

const localUser = { name: 'Alice', color: '#3b82f6' }

const editor = new Editor({
  extensions: [
    ConvertKit.configure({ table: false }),
    TableKit,
    Pages.configure({
      headerFooterExtensions: ctx => {
        const base = [ConvertKit.configure({ table: false }), TableKit]
        if (!ctx.isCollaborative || !ctx.ydoc) return base
        // Content sync only — see the callout below for why
        // CollaborationCaret is intentionally not forwarded here.
        return [...base, Collaboration.configure({ document: ctx.ydoc, field: ctx.field })]
      },
    }),
    Collaboration.configure({ document: doc }),
    // Awareness on the main document body.
    CollaborationCaret.configure({ provider, user: localUser }),
  ],
})
```

> **Why we don't put CollaborationCaret on every sub-editor:**
>
> `y-prosemirror`'s awareness has a **single cursor slot per client**. With one
> provider serving the main editor plus 8 header/footer sub-editors, every
> `yCursorPlugin` instance writes to that same slot, and each editor then tries
> to decode the others' positions against a different Y field. The render
> fallback paints a wide "selection" bar across the entire header / footer
> content rather than a thin caret.
>
> Keep `CollaborationCaret` on the main editor only. Content still syncs
> cleanly in every header/footer sub-editor; you just don't see remote carets
> while editing inside an overlay. The `ctx.cursor` value is still passed to
> the callback if you want to forward it to a single canonical sub-editor for
> experimentation — but don't forward it to all eight.

---

## What syncs

### Content

- **All eight header/footer sub-editors.** Default / first page / odd / even × header / footer — every subtype stays in sync.
- **Live previews on every client, even with the overlay closed.** When client A types in the header, client B's rendered page-level header updates within a frame — B does not need to open an overlay to see the change.
- **Cleared content stays cleared.** Deleting all header content on one client leaves the rendered header blank on every other client.
- **Heights adjust on remote growth.** If a remote user adds enough lines to push a header beyond its current reserved space, every client's page layout adjusts so the body doesn't overlap.

### Subtype toggles

- **`differentFirstPage`** (and its `differentFirstPageFooter` counterpart) toggled together on one client flip on the others, matching Microsoft Word's "both at once" behaviour.
- **`differentOddEven`** (and its `differentOddEvenFooter` counterpart) — same.

### Document-level page geometry

- **`pageFormat`** — built-in formats (`A4`, `Letter`, `Legal`, …) and custom `PageFormat` objects.
- **`pageGap`** — the vertical space between pages.
- **`headerTopMargin`** — explicit override of the distance from page top to header content.
- **`footerBottomMargin`** — explicit override of the distance from footer to page bottom.

A few practical examples:

```ts
// Triggering any of these on one client propagates to every connected client.
editor.commands.setPageFormat('Letter')
editor.commands.setPageGap(80)
editor.commands.setHeaderTopMargin(40)
editor.commands.setFooterBottomMargin(40)
```

The `reset*` margin commands propagate the **reset**, not the format default — `resetHeaderTopMargin()` clears the override on every client so they all fall back to the format's own default (50% of the format's top margin):

```ts
editor.commands.resetHeaderTopMargin()
editor.commands.resetFooterBottomMargin()
```

### Optional: awareness

If you wire `@tiptap/extension-collaboration-cursor` on the parent editor, remote carets and labels appear in the header/footer overlays too — see [Adding `CollaborationCursor`](#adding-collaborationcursor) below.

## What does NOT sync

These stay per-client by design, because they describe how *you* are viewing the document rather than the document itself:

- **`zoom`** — different screen sizes need different zoom. One user at 80%, another at 100% is fine and useful.
- **`accentColor` / `headerAccentColor` / `footerAccentColor`** — overlay chrome (caret, toolbar border, label background) only shows on *your* edits. Per-user lets people pick a high-contrast option for accessibility.
- **`pageGapBackground`** — visual chrome outside the page area, same reasoning as accent.
- **`editableHeader` / `editableFooter`** — these are permission gates. They should come from your app's auth/role model (admin can edit, viewer can't), not from per-document Y state. Otherwise any user could lock everyone out.

If you need any of these shared (for example, you want all clients to see the same accent), sync them through your own application state.

## Caveats

- **Don't pass `header` / `footer` defaults in `Pages.configure()` when collaboration is on.** With collaboration enabled the header/footer content is owned by Y, and a configured default can re-appear after another client has cleared the content. Leave the defaults out; if you need a starter value, set it once via `editor.commands.setHeader(...)` after the provider has synced.
- **The package does not bundle `yjs` or `@tiptap/extension-collaboration`.** They're peer dependencies — install them in your application and attach `Collaboration` through the `headerFooterExtensions` callback.
- **Toggling `differentOddEven` does not copy content across the wire.** When you enable it, no content is pre-populated from the default header into the odd slot for other clients. Each client sees what's actually been edited into each subtype.

---

## The callback context

The callback receives a context with everything it needs to wire collaboration for one subtype:

```ts
import type { HeaderFooterExtensionsContext } from '@tiptap-pro/extension-pages'

interface HeaderFooterExtensionsContext {
  /** Which sub-editor this invocation is for. */
  editorType: 'header' | 'footer'
  /** Which subtype (default / first / odd / even). */
  subType: 'default' | 'first' | 'odd' | 'even'
  /** True when the parent editor has Collaboration installed. */
  isCollaborative: boolean
  /** Y.Doc instance from the parent's Collaboration extension, or null. */
  ydoc: import('yjs').Doc | null
  /** Y field name to bind the sub-editor's Collaboration extension to. */
  field: string
  /** Awareness info from the parent's CollaborationCursor, or null. */
  cursor: { provider: unknown; user: unknown } | null
}
```

Pass `ctx.field` straight into `Collaboration.configure({ document: ctx.ydoc, field: ctx.field })` — the extension routes each subtype to its own slice of the `Y.Doc` automatically.

---

## Tips for a smooth experience

- **Wait for the provider to sync before allowing edits.** A common pattern is to gate `<EditorContent>` behind a `connected` flag that flips on the provider's `onSynced` callback.
- **Mirror the main editor's extensions in the callback's `base`.** Pass the same `ConvertKit` / `TableKit` configuration so the sub-editors accept the same marks and nodes.
- **Persistence is your job.** Tiptap Cloud / your provider persists the `Y.Doc`, which automatically covers the header/footer content, the synced toggles, and the document-level page geometry (format, gap, margin overrides). You only need to persist the main document body separately if you also want a non-collab fallback snapshot.
- **Test in two browser windows.** Open the same document in two tabs and exercise the full sync surface: type in a header on one and watch the other's page-level preview update without opening the overlay; toggle a different-first-page flag and watch both buttons flip together; change the page format on one and watch the other's pages reflow; drag a margin slider on one and watch the other's page CSS recompute.

## Next steps

- Explore [Pages options](https://tiptap.dev/docs/pages/core-concepts/options.md) for layout control
- See [Page header and footer](https://tiptap.dev/docs/pages/core-concepts/page-header-footer.md) for editing UX details, locking, programmatic open/close, and DOCX-aware behavior
- See the [Tiptap collaboration docs](https://tiptap.dev/collaboration) for advanced collaboration usage
