Block library
Settings → Block library lists every installed option. The package catalogue includes the sweep, compact and simple heroes beside the hero alias, and the feature highlights, feature checklist and testimonial carousel sections. A release that adds a block changes the array length, so run payload migrate:create with it. Editors browse categories and switch a card on or off. Off is editorial availability: the block stays in saved rows until something else removes it. Save is Payload’s document button.
Categories are a package list: Hero, Feature, Process, FAQ, Testimonials, Stats, Content, Contact, CTA, and Navigation. A site extra appears only in the category it names. megaMenu and navLink are header item blocks, not cards. Navigation’s card is floatingNav. While it is the only navbar, that card has no switch. A second header brings the switches back, so an editor picks which one the site uses.
The rail is a search box over an accordion of categories, each header reading Category (n) with every block in it counted, switched-off ones included. One category is open at a time; the gallery shows all of its cards, and each block under the open header is a #<slug> link to its card. The link for the card in view is marked as you scroll, and opening the Block library with #<slug> in the URL lands on that card. Searching matches a block’s name or slug, or a category’s name: every matching category opens with its match count, the gallery shows the matches under their category headings, and clearing the search returns to the category that was open. On a narrow screen the rail stacks above the gallery. The rail is CategoryRail from @bison-lab/admin-ui.
Each card’s island is an iframe of previewPath(slug). The reserved route that renders the sample under the published theme (themeHead) is the site’s. So is hiding an off slug from Add section.
Wrap the sample in BlockLibraryIsland (@bison-lab/payload-core/react) and the card is as tall as the block (BIS-147). The island pads the sample evenly and posts its height to the admin window; the card takes it from its own frame on its own origin only, between a floor (an empty or position: fixed sample stays a visible card) and a ceiling (a vh-sized sample cannot grow the card forever). An island that never reports keeps the fixed 720px card.
Turning a card off distinguishes published from draft. The last hero and the last navbar are always refused. A slug on a published page, or the navbar the published Header uses, is refused too: the message names the published pages and mentions drafts, and nothing is deleted. A slug only drafts hold asks first — the dialog names those drafts — and the Save that turns it off deletes those rows from the drafts, as new draft versions. Published pages are never written. An unused slug turns off with no dialog.
Under Enabled, each hero and section card carries four ticks: Background, Spacing, Anchor, Hidden, the controls that block’s Block Settings fold offers. A hero arrives with Anchor only and Hidden locked (a page cannot publish without its hero); a section arrives with all four; a navbar has none. Untick one and save, and that control leaves the fold on every page while its stored value is ignored at render. No page schema moves: the four columns stay on every block. A disabled block keeps its ticks. The ticks are stored on the row and seeded from the catalogue, so a block that arrives in a later release gets its default without a migration beyond the new columns. A page route reads them with getSectionControls(payload) from @bison-lab/payload-core/block-library and passes the map to RenderBlocks as sectionControls.
The Save does the work, not the dialog, so the same rules apply to a Save from the API. A draft whose only section was stripped cannot be published until a section is added. The Header’s navbar is header.navbar when the document has one; the package Header does not yet, so it always uses floatingNav, and that card cannot turn off while the Header is published.
import { createBlockLibrary } from "@bison-lab/payload-core";
export default buildConfig({ globals: [ createBlockLibrary({ previewPath: (slug) => `/block-library/${slug}`, blocks: [ { slug: "ctaBand", label: "Doctor finder", category: "cta", kind: "section", sectionControls: { spacing: false } }, ], }), ],});A site extra’s sectionControls is its catalogue default; a key left out follows kind. access.update defaults to Admin. Pass access when someone else may change availability. The field is BLOCK_LIBRARY_FIELD (@bison-lab/payload-core/admin#BlockLibraryField). Run payload generate:importmap and payload migrate:create.
The Add Block drawer
Section titled “The Add Block drawer”Payload’s own drawer opens from three places: a blocks field’s Add button, Add below in a row’s ⋯ menu, and the Preview overlay. Payload offers no slot to replace it, so blockDrawer() sets what it reads on every catalogue block:
- A category heading. Each block’s
admin.groupis its Block library category, and blocks are ordered the way the Block library lists them. Header item blocks are left alone. - A screenshot. Each block’s
admin.images.thumbnailis a picture of its sample, with generic copy and photos, under a neutral slate theme. The package ships one for every package page block and serves it from/api/block-library/thumbnail/<slug>to signed-in users. A site extra passes its ownthumbnailURL on its catalogue entry. A block that already sets a thumbnail keeps it. - Larger cards. Three across, two at 1024px and one at 768px, instead of Payload’s six.
import { blockDrawer, createBlockLibrary } from "@bison-lab/payload-core";
export default buildConfig({ globals: [ createBlockLibrary({ previewPath: (slug) => `/block-library/${slug}`, blocks: [ { slug: "ctaBand", label: "CTA band", category: "cta", thumbnail: "/block-thumbnails/ctaBand.jpg" }, ], }), ], plugins: [blockDrawer()],});Run payload generate:importmap for the drawer stylesheet. Which blocks appear is still the site’s filterOptions.
Making a site’s screenshots
Section titled “Making a site’s screenshots”bison-block-thumbnails (a bin on @bison-lab/payload-core) opens each block’s Block library preview in a headless browser at desktop width, lays the neutral theme over the site’s, and saves a 960 × 640 JPEG per block. Run it from the site, which supplies Playwright, against a running server and a login the preview route accepts, and serve the files from public/:
THUMBNAIL_EMAIL=… THUMBNAIL_PASSWORD=… pnpm exec bison-block-thumbnails \ --base http://localhost:3030 --slugs ctaBand,productMap \ --out-dir public/block-thumbnailsRerun it when a block’s layout or sample changes. Give a site block generic sample copy, so its card reads as no particular business. A block that reads records from the database draws whatever that database holds; add a query to --preview (/block-library/{slug}?thumbnail=1) and have the preview route swap in generic stand-ins when it sees it.