Skip to content

Entry points

The Payload packages straddle the server and client boundary, so each ships more than one entry. Importing from the wrong one is the most common wiring mistake. @bison-lab/admin-ui is a single entry and stays free of Payload.

Import Contents Runs where
@bison-lab/payload-blocks Block configs, field builders, row types, resolveMedia, orphanBlockTypes, blockSamples, sampleImage, lookField, colorTokenField Node. What payload.config.ts imports. Touches no React.
@bison-lab/payload-blocks/hooks Collection hooks a site adds itself: saveFaqsToLibrary Server. Imports Payload’s error classes as values, so only payload.config.ts and its collections import it; the main entry stays safe for a client component to reach.
@bison-lab/payload-blocks/react Renderers, RenderBlocks and BLOCK_ID_ATTRIBUTE, the image and link seams, resolveLink and ResolveLink, headerFromNavigation, headerItemsFromBlocks, the seam constants, the rendering types Client ("use client").
@bison-lab/payload-blocks/rich-text RichTextBlockRenderer, richTextBlockRenderer() to build one that resolves internal links, and internalDocToHrefFrom Client. Split out because it is the only thing that needs @payloadcms/richtext-lexical.
@bison-lab/payload-blocks/admin MinRowsArrayField, ItemsField, LinkField, NavItemsField, AdminWorkspace, FooterColumnsField, the client half of SectionSettingsField Client, inside the Payload admin. Resolved through the site’s import map, never imported by hand.
@bison-lab/payload-blocks/admin-server SectionSettingsField: the Block Settings fold, which reads the Block library with payload and hands the client fold the block’s controls Server, inside the Payload admin. No "use client" banner. Resolved through the import map.

The renderers are client components because the blocks they render are: every @bison-lab/ui export is a client reference, and the blocks are interactive anyway. That is also what makes imageComponent work. A Server Component can hand a client component reference across the boundary, but not a closure.

The admin entries are referenced by string, never imported: a config names MIN_ROWS_ARRAY_FIELD, ITEMS_FIELD, LINK_FIELD, NAV_ITEMS_FIELD, ADMIN_WORKSPACE or SECTION_SETTINGS_FIELD (exported from the main entry) as a field or view component, and payload generate:importmap turns the string into the import. A missing entry renders the field as nothing, not as the stock field. SECTION_SETTINGS_FIELD points at admin-server, so a site upgrading from a release where it pointed at admin regenerates its import map.

Import Contents Runs where
@bison-lab/payload-core seoPlugin, createTheme, createBrandAssets, createRoles, createFeatures, createBlockLibrary, createPages, createUsers, createMedia, createRedirects, createQuestions, createTaxonomy, facetField, createAdminNav, adminNav, createNavigation, adminOnlyApiTab, documentTitleActions, listAlign, richTextEditorFonts, predicates, noIndexField, SHARE_IMAGE_SIZE, firstImageIn, title and text helpers, import-map strings (QUESTIONS_LIST, BLOCK_LIBRARY_FIELD), types Node. What payload.config.ts imports; it loads the SEO plugin.
@bison-lab/payload-core/metadata pageMetadata, the title helpers, the same types Server. What a page route imports; it does not load the plugin.
@bison-lab/payload-core/theme getPublishedTheme, getPublishedIdentity, themeConfigFromDoc, themeHead, themeHeadFromDoc Server. What a root layout imports; it does not load the plugin or React.
@bison-lab/payload-core/navigation getNavigation, the Header and Footer slugs Server. What a site header imports; it does not load the plugin or React.
@bison-lab/payload-core/block-library getSectionControls, sectionControlsFromLibrary, SectionControls, the catalogue readers Server. What a page route and the Block Settings fold import; it does not load the plugin or React.
@bison-lab/payload-core/taxonomy facetField, FacetEntry Anywhere. What a block config imports to tag content from a taxonomy; it does not load the plugin or React, so a browser bundle can take it. facetField is also on the main entry.
@bison-lab/payload-core/block-thumbnails captureThumbnails, thumbnailCss, NEUTRAL_THEME, thumbnailVersion, and the bison-block-thumbnails bin Node, beside Playwright. What a site’s Add Block screenshot script uses; it does not load Payload or React.
@bison-lab/payload-core/wireframe Deprecated: captureWireframes, wireframeSvg, and the bison-wireframes bin Node. Kept for one release; use ./block-thumbnails. Goes in the next major.
@bison-lab/payload-core/admin Theme fields, the Roles and Features matrices, BlockLibraryField, Admin nav, slug, Users Roles checklist, LookField, QuestionsList, MediaListView, MediaAltAutofill Admin. Referenced by import-map string; generate:importmap writes it.
@bison-lab/payload-core/react ThemePreview (legacy; Theme has no preview pane) Client. Kept so an older site import does not break.
Import Contents Runs where
@bison-lab/tokens ThemeConfig and buildThemeCss, the palette derivation (deriveLightPalette, deriveDarkPalette), colour scales, the contrast audit (auditTheme), and the preset catalogues Anywhere. No React, no DOM.
@bison-lab/tokens/dom attachEdgeShade, edgeShadeFractions and the shade custom-property names Browser. Framework-free DOM helpers only — no React, no Tailwind, nothing theme-derived.

The css/* subpaths ship the stylesheets themselves (css/globals.css, css/base.css, css/themes/*, css/animations.css, css/utilities.css).

/dom is split off so @bison-lab/admin-ui can share a helper without reaching the palette derivation on the main entry: the visitor library and the admin kit deliberately do not share brand tokens. It is what keeps a pinned column’s scroll shade identical in both — @bison-lab/ui’s Table and the kit’s Table compute it from the same function and write the same two custom properties, then style it with their own CSS. @bison-lab/ui imports it at runtime; the kit bundles it at build time, so installing @bison-lab/admin-ui still pulls in nothing but radix-ui.

Import Contents Runs where
@bison-lab/admin-ui KitProvider, type cards, dialog, confirm, Popover, Chip, ActionMenu, button, badge (variant={false} skips a preset), checkbox, input, switch, textarea, stacked Field / Label, Kicker, Fold, Stack, select, Table (pin / shade on cells), Thumbnail, Dropzone, Pagination, rail glyphs (Glyph variants or variant={false}), workspace chrome (Page for replaced lists, Canvas, ItemRail, MainHead, RailSlot) Client. Payload-free. Wrap a screen in KitProvider once. Replaced collection lists wrap Page.

The payload-blocks main entry exports the builders the configs are made of, for a site’s own blocks:

  • imageField({ name, required, relationTo, admin }) for a single upload from the media collection.
  • linkFields({ required, pagesCollection }) for a full link: label, the destination row, and newTab. linkField({ name }) is the same as one named group. linkDestinationFields() is the destination row alone: type, page and href, carrying LINK_FIELD so the editor picks a published page or pastes a URL in one box. pagesCollection points a page link at a collection other than pages.
  • resolveLink(link) and linkTypeOf(link) read a destination the way every renderer and the picker do, so a site’s own code agrees with them on every row, including rows from before links had a type.
  • headingFields({ required }) for the eyebrow, title and description every band opens with.
  • emptyRows(n) and MIN_ROWS_ARRAY_FIELD for an array that opens with n rows and will not go below them.
  • ITEMS_FIELD and itemsFieldAdmin({ title, thumbnail, meta, numbered, link, initials, layout, pair }) for an array edited with the admin kit’s items list instead of Payload’s collapsible rows.
  • lookField() and colorTokenField({ name }) read the published Theme. Options are the scales Color Settings offers page editors (Primary, Secondary, Accent, Highlight, plus every custom color) and only the included steps. Success and Destructive stay off that list. The picker is LOOK_FIELD (@bison-lab/payload-core/admin#LookField).
  • resolveMedia(value) narrows an upload value, resolved or not, to { src, alt, width, height } or undefined.

Neither Payload package can import a site’s payload-types, so every row shape is hand-written and structural: every field optional and nullable, no index signatures. A generated title: string is assignable to title?: string | null, never the reverse. Sites pass their generated types in, as BlockRegistryFor<AnyBlock> and seoPlugin<Page>() show.