# Payload Nested Docs Plugin [![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-nested-docs)](https://www.npmjs.com/package/@payloadcms/plugin-nested-docs) A plugin for [Payload CMS](https://github.com/payloadcms/payload) to easily allow for documents to be nested inside one another. Core features: - Allows for [parent/child](#parent) relationships between documents - Automatically populates [breadcrumbs](#breadcrumbs) data ## Installation ```bash yarn add @payloadcms/plugin-nested-docs # OR npm i @payloadcms/plugin-nested-docs ``` ## Basic Usage In the `plugins` array of your [Payload config](https://payloadcms.com/docs/configuration/overview), call the plugin with [options](#options): ```js import { buildConfig } from "payload/config"; import nestedDocs from "@payloadcms/plugin-nested-docs"; const config = buildConfig({ collections: [ { slug: "pages", fields: [ { name: "title", type: "text", }, { name: "slug", type: "text", }, ], }, ], plugins: [ nestedDocs({ collections: ["pages"], generateLabel: (_, doc) => doc.title, generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ""), }), ], }); export default config; ``` ### Fields #### Parent The `parent` relationship field is automatically added to every document which allows editors to choose another document from the same collection to act as the direct parent. #### Breadcrumbs The `breadcrumbs` field is an array which dynamically populates all parent relationships of a document up to the top level. It does not store any data in the database, and instead, acts as a `virtual` field which is dynamically populated each time the document is loaded. The `breadcrumbs` array stores the following fields: - `label` The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generateLabel). - `url` The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateURL). ### Options #### `collections` An array of collections slugs to enable nested docs. #### `generateLabel` Each `breadcrumb` has a required `label` field. By default, its value will be set to the collection's `admin.useAsTitle` or fallback the the `ID` of the document. You can also pass a function to dynamically set the `label` of your breadcrumb. ```js { plugins: [ ... nestedDocs({ ... generateLabel: (_, doc) => doc.title // NOTE: 'title' is a hypothetical field }) ] ``` The function takes two arguments and returns a string: 1. `breadcrumbs` - an array of the breadcrumbs up to that point 2. `currentDoc` - the current document being edited #### `generateURL` A function that allows you to dynamically generate each breadcrumb `url`. Each `breadcrumb` has an optional `url` field which is undefined by default. For example, you might want to format a full URL to contain all of the breadcrumbs up to that point, like `/about-us/company/our-team`. ```js plugins: [ ... nestedDocs({ ... generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''), // NOTE: 'slug' is a hypothetical field }) ] ``` This function takes two arguments and returns a string: 1. `breadcrumbs` - an array of the breadcrumbs up to that point 1. `currentDoc` - the current document being edited #### `parentFieldSlug` When defined, the `parent` field will not be provided for you automatically, and instead, expects you to add your own `parent` field to each collection manually. This gives you complete control over where you put the field in your admin dashboard, etc. Set this property to the `name` of your custom field. #### `breadcrumbsFieldSlug` When defined, the `breadcrumbs` field will not be provided for you, and instead, expects your to add your own `breadcrumbs` field to each collection manually. Set this property to the `name` of your custom field. > Note - if you opt out of automatically being provided a `parent` or `breadcrumbs` field, you need to make sure that both fields are placed at the top-level of your document. They cannot exist within any nested data structures like a `group`, `array`, or `blocks`. ## Overrides You can also extend the built-in `parent` and `breadcrumbs` fields per collection by using the `createParentField` and `createBreadcrumbField` methods. They will merge your customizations overtop the plugin's base field configurations. ```js import { CollectionConfig } from "payload/types"; import createParentField from "@payloadcms/plugin-nested-docs/fields/parent"; import createBreadcrumbsField from "@payloadcms/plugin-nested-docs/fields/breadcrumbs"; const examplePageConfig: CollectionConfig = { slug: "pages", fields: [ createParentField( // First argument is equal to the slug of the collection // that the field references "pages", // Second argument is equal to field overrides that you specify, // which will be merged into the base parent field config { admin: { position: "sidebar", }, // Note: if you override the `filterOptions` of the `parent` field, // be sure to continue to prevent the document from referencing itself as the parent like this: // filterOptions: ({ id }) => ({ id: {not_equals: id }})` } ), createBreadcrumbsField( // First argument is equal to the slug of the collection // that the field references "pages", // Argument equal to field overrides that you specify, // which will be merged into the base `breadcrumbs` field config { label: "Page Breadcrumbs", } ), ], }; ``` ## TypeScript All types can be directly imported: ```js import { nestedDocsConfig, GenerateURL, GenerateLabel, } from "@payloadcms/plugin-nested-docs/dist/types"; ``` ## Screenshots