Building sections
Blocks
Rendering a section’s blocks from a type map.
A section with blocks maps type → component instead of growing a switch:
1import { Blocks } from '@/framework'23const BLOCKS = {4 title: ({ settings, editor, context }) => (5 <h1 {...editor}>{settings.text || context.product.title}</h1>6 ),7 price: PriceBlock,8 accordion: AccordionBlock,9}1011export default function MainProduct({ blocks, product }) {12 return <Blocks blocks={blocks} components={BLOCKS} context={{ product }} />13}Each component receives:
{ block, settings, editor, context, index }Spread editor onto your own root
1function TitleBlock({ settings, editor, context }) {2 return <h1 {...editor}>{settings.text || context.product.title}</h1>3}editor carries Shopify's shopify_attributes, which is what makes Inspect and
block selection land on this block rather than the whole section.
<Blocks> deliberately does not wrap anything. Wrapping each block in a
div would be easier, but it would put an element the section's author never
wrote into their layout — fatal inside a flex or grid container, where an extra
level changes the whole arrangement.
Why a map rather than a switch
A switch has to remember, in every branch, to spread the attribute. A branch
that forgets produces a block the theme editor cannot select — which looks like
the editor is broken rather than like a missing spread. The map does the lookup,
the key and the attribute parsing once.
A block type with no component renders nothing, and says so in the console while in the theme editor. Silence there is the worse failure: a merchant adds a block, nothing appears, and there is nothing to search for.
Placing blocks in more than one region
Some sections put blocks in two places — a column plus a row of tabs underneath.
groupBlocksByType splits the list, and <Block> renders one at a time:
1import { Block, groupBlocksByType } from '@/framework'23const groups = groupBlocksByType(blocks)45<aside>6 {(groups.get('tab') ?? []).map((block, index) => (7 <Block key={block.id} block={block} index={index} components={BLOCKS} context={ctx} />8 ))}9</aside>A fallback for unknown types
<Blocks blocks={blocks} components={BLOCKS} context={ctx} fallback={<Placeholder />} />Useful while a section is half-built, so an unimplemented type shows something rather than a gap.