Skip to Content
Delmare Digital
Guides

Extensibility

Theme the tree outside the Payload admin, send Edit links to your own editor, and build custom tree UIs with buildTreeStructure.

The tree's components and helpers are exported, so you can restyle the tree, send its Edit links somewhere else, or build your own view of the same data.

Theming outside the Payload admin

The tree components use Payload's CSS variables. When you render them outside the admin panel, import the default theme:

import '@delmaredigital/payload-page-tree/theme.css'

To match another design system, add your own wrapper around PageTreeClient and map the variables on it. The plugin does not render a .page-tree-wrapper class. For shadcn/ui:

components/ThemedTree.tsx
'use client'

import type { ComponentProps } from 'react'
import { PageTreeClient } from '@delmaredigital/payload-page-tree/client'
import '@delmaredigital/payload-page-tree/theme.css'

export function ThemedTree(props: ComponentProps<typeof PageTreeClient>) {
  return (
    <div className="page-tree-wrapper">
      <PageTreeClient {...props} />
    </div>
  )
}
globals.css
.page-tree-wrapper {
  --theme-bg: hsl(var(--background));
  --theme-input-bg: hsl(var(--background));
  --theme-elevation-0: hsl(var(--background));
  --theme-elevation-50: hsl(var(--muted));
  --theme-elevation-100: hsl(var(--border));
  --theme-elevation-150: hsl(var(--border));
  --theme-elevation-400: hsl(var(--muted-foreground));
  --theme-elevation-500: hsl(var(--muted-foreground));
  --theme-elevation-600: hsl(var(--foreground));
  --theme-elevation-700: hsl(var(--foreground));
  --theme-elevation-800: hsl(var(--foreground));
  --theme-success-500: hsl(var(--primary));
  --theme-error-500: hsl(var(--destructive));
  --theme-error-50: hsl(var(--destructive) / 0.1);
}

Custom edit URLs

Change where Edit links go, for example to a visual editor, by passing getEditUrl to PageTreeClient:

import { PageTreeClient, type GetEditUrlFn } from '@delmaredigital/payload-page-tree/client'

const getEditUrl: GetEditUrlFn = (collection, id, adminRoute) => {
  return `${adminRoute}/my-editor/${collection}/${id}`
}

<PageTreeClient getEditUrl={getEditUrl} /* treeData, collections, selectedCollection, adminRoute */ />

GetEditUrlFn is (collection: string, id: string, adminRoute: string) => string. id is the raw document ID and adminRoute is your admin prefix, such as /admin. Without it, the tree uses Payload's standard collection URLs.

PageTreeClient takes treeData, collections, selectedCollection and adminRoute, plus optional puckEnabled and getEditUrl.

Building custom tree views

buildTreeStructure turns folders and pages into the same nested TreeNode[] the tree view uses:

import { buildTreeStructure, type TreeNode } from '@delmaredigital/payload-page-tree'

// Fetch folders and pages from Payload
const folders = await payload.find({ collection: 'payload-folders', limit: 0 })
const pages = await payload.find({ collection: 'pages', limit: 0 })

// Build the tree
const tree: TreeNode[] = buildTreeStructure(
  folders.docs,
  pages.docs.map((p) => ({ ...p, _collection: 'pages' })),
  { collections: ['pages'] },
)

Node ids are prefixed (folder-1, page-1) because folders and pages can share numeric IDs; rawId holds the unprefixed database ID for API calls.

Package entry points

ImportContents
@delmaredigital/payload-page-treepageTreePlugin (also the default export), buildTreeStructure, getRedirectMap, getFolderPath, slugify, withPageTreeAccess, defaultPageTreeAccess, and the types PageTreePluginConfig, BuildSlugFn, BuildSlugArgs, PageTreeAccess, PageTreeAccessArgs, TreeNode, FolderDocument, PageDocument, SlugHistoryEntry, SlugChangeReason, BuildTreeOptions
@delmaredigital/payload-page-tree/clientPageTreeClient, GetEditUrlFn, PageTreeNavLink, TreeNode (component), ContextMenuProvider
@delmaredigital/payload-page-tree/rscPageTreeView (the admin view server component)
@delmaredigital/payload-page-tree/theme.cssDefault theme variables
Edit this page on GitHub

On this page