The block picker
`.admin()` is the only thing standing between an editor and thirty unlabelled block types. It sets the name, the icon and the group each block appears under.
A block with no .admin() still works. It shows up in the sidebar under
"Other", wearing its type name and a default cube, which is fine for two blocks
and unusable at twenty.
The options
.admin() takes a config object, or a function given { c }, the component
proxy that c.icon() comes from. Icons are Iconify names, resolved by the
admin at render time.
| Option | Type | Effect |
|---|---|---|
label | I18nText | The name in the picker. |
description | I18nText | Help text beside it. |
icon | ComponentReference | c.icon("ph:image"). |
category | BlockCategoryConfig | The group: { label, icon?, order? }. |
order | number | Position inside the group, lowest first. |
hidden | boolean | Keeps the block out of the picker. |
An I18nText can be a plain string or a map of locale to string, so
label: { en: "Hero", sk: "Hero sekcia" } is as valid as label: "Hero".
.admin(({ c }) => ({
label: { en: "Hero Section", sk: "Hero sekcia" },
description: { en: "Full-width image with a headline" },
icon: c.icon("ph:image"),
category: { label: "Sections", icon: c.icon("ph:layout"), order: 1 },
order: 1,
}))How the groups come out
Blocks are grouped by their category label, lowercased. Two blocks naming the category "Sections" land together whatever else they pass, and the first one seen supplies the icon and order for the whole group.
Groups sort by the category's order, blocks sort by their own inside it, and
anything without an order falls back to 999. A block with no category at all
joins "Other", which carries 999 as well and lands at the bottom.
Because the label is the key, spell it once and import it.
import type {
AdminConfigContext,
BlockCategoryConfig,
} from "@questpie/admin/factories";
export const sections = (c: AdminConfigContext["c"]): BlockCategoryConfig => ({
label: "Sections",
icon: c.icon("ph:layout"),
order: 1,
});Then every block in the folder says category: sections(c) and the group holds
together on its own. A file starting with _ is not a block, so the helper can
live beside them.
Retiring a block
hidden: true takes a block out of the picker while leaving it registered.
Pages that already use it keep rendering, and nobody can add another one.
Deleting the file instead leaves those pages holding a type nothing answers to:
the editor shows "Unknown block type" and your frontend renders nothing.
Laying out the fields
Where .admin() handles the picker, .form() handles the panel an editor sees
after choosing the block. It takes the same layout shapes a collection form
takes, referencing fields by name through the f proxy.
.form(({ f }) => ({
fields: [
{ type: "section", label: "Content", fields: [f.title, f.subtitle] },
{
type: "section",
label: "Layout",
layout: "grid",
columns: 2,
fields: [f.alignment, f.height],
},
],
}))Without .form() the fields render in declaration order, which is often
enough.
Related
- Blocks for the builder these two methods sit on.
- Admin collections for the same layout shapes on a collection form.