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:
'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>
)
}.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
| Import | Contents |
|---|---|
@delmaredigital/payload-page-tree | pageTreePlugin (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/client | PageTreeClient, GetEditUrlFn, PageTreeNavLink, TreeNode (component), ContextMenuProvider |
@delmaredigital/payload-page-tree/rsc | PageTreeView (the admin view server component) |
@delmaredigital/payload-page-tree/theme.css | Default theme variables |
Organization scoping
Give each organization its own folders in a multi-tenant app by adding access rules and an organization field with customizeFolderCollection.
Adding to existing projects
Add payload-page-tree to a project that already has pages and folders without changing any live URL until you choose to.

