10x-media plugins
Admin Wiki

Configuration

The complete option reference for adminWiki(), plus access control and the endpoints it registers.

Every option is optional. adminWiki({}) is a working configuration.

payload.config.ts
import { adminWiki } from '@10x-media/admin-wiki'

adminWiki({
  access: { create: ({ req }) => req.user?.role === 'editor' },
  chips: { blocks: true },
  editor: { blocks: [{ block: tipBlock, component: '/components/TipBlock#TipBlock' }] },
  exclude: { collections: ['users'], globals: ['nav'], blocks: ['spacer'] },
  featured: true,
  hidden: { media: true },
  localeMap: { 'en-US': 'en' },
  overrides: { pages: { tabs: [reviewTab] } },
  slugs: { pages: 'wiki-pages', media: 'wiki-media' },
  triggers: {
    edit: true,
    global: true,
    list: { slot: 'afterListTable' },
  },
  video: true,
  wikiView: true,
  writeAffordances: 'editMode',
})

Options

OptionTypeDefaultPurpose
disabledbooleanfalseReturn the incoming config untouched. Switch the plugin off per environment without removing the call
accessWikiAccessOptionslogged-in for all fourAccess predicates for the wiki collections. See Access
chips{ blocks }blocks onWhat the "Covers" chips show on the wiki views. See The wiki view
customTargetsWikiCustomTargetOption[][]Surfaces the config does not describe, so guides can be attached to them too. See Custom targets
editor{ blocks, inlineBlocks, features, converters }{}Project blocks and inline blocks available in the wiki editor, each with its renderer, plus lexical features beside the plugin's own and the converters that render them. See Authoring
exclude{ blocks, collections, globals }Payload's internalsEntities the plugin leaves alone entirely. See Exclusions
featuredbooleantrueLead the list band with the collection's featured guides as cards
hidden{ media, pages }visiblePayload's admin.hidden passthrough, per wiki collection
localeMapRecord<string, string>{}Map admin UI languages to content locales. See Targeting
overrides{ media, pages }{}Reshape the collections the plugin registers. See Collection overrides
slugs{ media, pages }wiki-media, wiki-pagesCollection slug overrides
translationsTranslationsOptionbuilt-insPer-locale string overrides. See i18n
triggersWikiTriggersOptionsall onWhich surfaces carry a guide affordance. See Triggers
videoboolean | { playerComponent }falseVideo uploads and YouTube/Vimeo embeds. See Authoring
wikiViewboolean | { components }trueThe /wiki browsing and reading views, and your own components in the index slots. See The wiki view
writeAffordances'always' | 'editMode' | 'never''editMode'When "write this guide" renders. See Write affordances

Exclusions

exclude names entities the plugin does not touch: no help on their fields, no guides panel, no list band, no block help, and no entry in the target pickers, so a guide cannot be attached to one even in wiki edit mode.

adminWiki({
  exclude: {
    blocks: ['spacer'],
    collections: ['users'],
    globals: ['nav'],
  },
})

The three lists are separate because Payload only enforces slug uniqueness within one kind. A collection, a global, and a block may all be called settings, and one flat list would exclude three unrelated things at once.

These add to what is already excluded:

Always excludedSlugs
The wiki's own collectionsWhatever slugs.pages and slugs.media resolve to
Payload's bookkeeping collectionspayload-folders, payload-jobs, payload-locked-documents, payload-migrations, payload-preferences, payload-query-presets
Payload's bookkeeping globalspayload-jobs-stats

Both built-in lists are exported as PAYLOAD_INTERNAL_COLLECTIONS and PAYLOAD_INTERNAL_GLOBALS.

Excluding an entity does not remove guides already targeting it. Their targets stop resolving, so the orphan banner lists them until you edit or delete them.

To leave a single field out without excluding its whole collection, give that field its own Description component; see Customization.

Triggers

triggers chooses which surfaces carry a guide affordance across everything the plugin covers:

adminWiki({
  triggers: {
    edit: true,                          // sidebar panel on collection documents
    global: true,                        // sidebar panel on globals
    list: { slot: 'afterListTable' },    // band on collection lists
  },
})

list accepts true (the band at its default slot), false (no band), or an object choosing the slot: beforeList, beforeListTable, afterListTable, or afterList. Only the list band takes a slot option.

Turning a trigger off keeps the entity in the wiki: its fields still carry help and it is still a valid target. To take an entity out of the wiki altogether, use exclude.

Collection overrides

Guides are Payload documents, so projects eventually want their own fields on them: review state, owners, a category. overrides reshapes the two collections the plugin registers, after they are fully built and before Payload sees them.

Fields added this way are filled at seed time through additionalData.

Tabs on the guide pages

Guide authoring is already split into a Guide tab and a Targets tab, so extra fields belong in tabs of their own rather than loose at the end of the form. overrides.pages.tabs appends to that list:

adminWiki({
  overrides: {
    pages: {
      tabs: [
        {
          label: 'Editorial',
          fields: [
            { name: 'tags', type: 'text', hasMany: true },
            { name: 'reviewedAt', type: 'date' },
          ],
        },
      ],
    },
  },
})

Named and unnamed tabs both work. An unnamed tab writes its fields at the document root; a named tab nests them under the tab name, which is the safer choice if you are worried about colliding with a future plugin field.

The whole collection

overrides.pages.collection and overrides.media are plain functions: they receive the finished collection and return the one Payload registers. Nothing is merged afterwards, so this is the way to add sidebar fields, hooks, admin components, or list columns:

adminWiki({
  overrides: {
    pages: {
      collection: (collection) => ({
        ...collection,
        admin: { ...collection.admin, defaultColumns: ['title', 'owners', 'updatedAt'] },
        fields: [
          ...collection.fields,
          {
            name: 'owners',
            type: 'relationship',
            relationTo: 'users',
            hasMany: true,
            admin: { position: 'sidebar' },
          },
        ],
      }),
    },
    media: (collection) => ({
      ...collection,
      upload: { ...collection.upload, imageSizes: [{ name: 'thumb', width: 400 }] },
    }),
  },
})

Both run last, after the target fields have been populated from the walked config and the endpoints attached, so collection.fields includes everything the plugin built (tabs included, appended tabs and all).

Nothing is validated on the way out. Returning a collection without the plugin's slug, endpoints, versions.drafts, or target* fields will break the wiki, and the plugin will not warn you. Spread the collection you were given and add to it.

The media collection takes only the function form. An upload collection is one alt field and a file, so a tab shorthand would have nothing to organize.

Plugin order

The plugin documents the config as it stands when it runs, so it belongs last in the plugins array, after anything that adds collections or fields:

plugins: [
  fields({ /* ... */ }),
  adminWiki({}),
]

If the walk finds no fields at all, the plugin logs a warning on boot rather than failing. In practice that means the plugin ran before the config had any fields to document.

Access

Both wiki collections default to "any logged-in user" for create, read, update, and delete, never public. Restrict authoring in production:

adminWiki({
  access: {
    create: ({ req }) => req.user?.role === 'editor',
    update: ({ req }) => req.user?.role === 'editor',
    delete: ({ req }) => req.user?.role === 'admin',
    // read: omitted, so any logged-in user can read guides
  },
})

Each predicate receives Payload's standard access args including req, and therefore req.locale, so authoring can be restricted per role and per locale.

These predicates drive the UI as well as the data. The targets map returns the reader's evaluated create and update results, which is what decides whether "write this guide" and "edit guide" render. Read access is never overridden: the views, the drawers, and the endpoints all query as the requesting user with overrideAccess: false.

Endpoints

Two endpoints are registered on the wiki pages collection:

EndpointPurpose
GET /api/wiki-pages/targets-map?language=<lang>The map of target key to published guides for one reader, plus their evaluated create and update permissions. Guide content is not included
GET /api/wiki-pages/orphaned-targetsGuides whose stored targets no longer resolve against the running config. Requires update access; includes drafts

Both send cache-control: no-store and honor your read access. Paths follow slugs.pages when you override it.

Requirements

Payload ^3.83.0, React 19, Node 22.18 or newer. @payloadcms/richtext-lexical and @payloadcms/ui are required peers; @payloadcms/next and next are optional peers, needed only for the wiki view.

Run payload generate:importmap after installing and after upgrading. The plugin's surfaces are import-map component references, and a stale map is the most common reason nothing renders.

On this page