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'
2
3const BLOCKS = {
4 title: ({ settings, editor, context }) => (
5 <h1 {...editor}>{settings.text || context.product.title}</h1>
6 ),
7 price: PriceBlock,
8 accordion: AccordionBlock,
9}
10
11export 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'
2
3const groups = groupBlocksByType(blocks)
4
5<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.