Blocks
Creating Blocks
Build create catalogs with create component maps and composable create primitives.
Create flows use two layers:
RenderCreateBlocksrenders available block create components in registry order.CreateBlock*primitives compose the dialog, trigger, fields, cancel, and submit controls for one block type.
Create Component Maps
Create components are keyed by slug, just like preview and inline renderers.
const createComponents = {
hero: HeroCreateBlock,
summary: SummaryCreateBlock,
totals: TotalsCreateBlock,
} satisfies InferBlockCreateComponents<typeof pageBlocks>;Render them with one shared mutation and variable mapper.
<RenderCreateBlocks
registry={pageBlocks}
components={createComponents}
variables={(block) => ({ pageId, block })}
mutation={createBlockMutation}
/>RenderCreateBlocks uses registry.definitions for ordering because there are
no stored rows yet.
Create Component Props
Use BlockCreateComponentProps to avoid repeating mutation prop shapes.
type HeroCreateInput = {
slug: typeof heroBlock.slug;
data: z.input<typeof heroBlock.schema>;
};
type HeroCreateBlockProps<
TData = unknown,
TError = DefaultError,
TVariables = undefined,
TOnMutateResult = unknown,
> = BlockCreateComponentProps<HeroCreateInput, TData, TError, TVariables, TOnMutateResult>;A Block Create Form
Each create component owns its form schema, defaults, labels, and field layout.
export function HeroCreateBlock({ variables, mutation }: HeroCreateBlockProps) {
const form = useForm<HeroCreateValues>({
resolver: standardSchemaResolver(heroBlock.schema),
defaultValues: {
titlePart1: "Make space",
titlePart2: "for growth",
},
});
return (
<CreateBlock>
<CreateBlockTrigger>
<CreateBlockTriggerContent>
<CreateBlockTriggerTitle>{heroBlock.label}</CreateBlockTriggerTitle>
<CreateBlockTriggerDescription>{heroBlock.description}</CreateBlockTriggerDescription>
</CreateBlockTriggerContent>
</CreateBlockTrigger>
<Form {...form}>
<CreateBlockContent
variables={(data) => variables({ slug: heroBlock.slug, data })}
mutation={mutation}
>
<CreateBlockHeader>
<CreateBlockTitle>Add hero block</CreateBlockTitle>
</CreateBlockHeader>
<CreateBlockFields>{/* app-owned form fields */}</CreateBlockFields>
<CreateBlockFooter>
<CreateBlockCancel>Cancel</CreateBlockCancel>
<CreateBlockSubmit>Create block</CreateBlockSubmit>
</CreateBlockFooter>
</CreateBlockContent>
</Form>
</CreateBlock>
);
}What the Package Handles
- dialog open state
- form submission through TanStack Query mutation options
- resetting the nearest React Hook Form after success or cancel
- disabling submit while pending
- consistent trigger, field, footer, and action layout primitives
What the App Handles
- field components
- default values
- localization
- toasts
- query invalidation metadata
- the server mutation implementation