Creating new block types for the landing page builder
The landing page builder is a tool that allows content editors to create and edit landing pages in Sanity Studio from tailor-made blocks. It's built on top of Sanity's Array field (opens in a new tab).
When creating new blocks for the landing page builder, rely on tinloof-remix
CLI to scaffold the necessary files for you by running npm run cli createBlock
:
? What do you want to do? Create new block (createBlock)
? Block name (ex: "block.hero") Ideally prefixed with "block." block.newBlock
? Editor-friendly title (ex: "Initial section (Hero)") New Block (Example)
? Name of the front-end component (ex: "Hero") Can't have spaces or special characters. NewBlock
✅ Block schema created at /app/sanity/schemas/blocks/blockNewBlock.ts
✅ Block schema included in the studio at /app/sanity/schemas/blocks/index.ts
✅ Typescript data definition added to /app/types/block.types.ts
✅ Component created at /app/components/blocks/NewBlock.tsx
💡 Go through each of the files above and follow the @TODOs specified in them
What the command does
Creates a new block schema schema
The schema defines how the blocks of this type will be edited in the Sanity Studio.
If you're unfamiliar with Sanity schemas, check out the official documentation (opens in a new tab).
import { PackageIcon } from '@sanity/icons'
import { blockSchema } from '../blockSchema'
export const blockNewBlock = blockSchema({
name: 'block.newBlock',
title: 'New Block (Example)',
type: 'object',
// @TODO: replace with descriptive icon
icon: PackageIcon,
custom: {
// @TODO: populate the explainer following other blocks' examples
variants: undefined,
},
fields: [
// @TODO: populate block's schema
{
name: 'title',
title: 'Title',
type: 'string',
validation: (Rule) => Rule.required(),
},
],
})
Adds the block to Sanity's schema
The new block type is added to the blocks
array in app/sanity/schemas/blocks/index.ts
, so it can be used in the Studio:
// ...
import { blockNewBlock } from './blockNewBlock'
// %CLI/INJECT-IMPORT%
export default [
// ...
blockNewBlock,
// %CLI/INJECT-VARIABLE%
]
Creates a React component for rendering the block in the front-end
import type { NewBlockBlockData } from '~/types'
import { PropsDisplayer } from '../PropsDisplayer'
const NewBlock = (props: NewBlockBlockData) => {
// @TODO: validate block content before rendering
if (!props.title) return null
// @TODO: build the component, but don't forget to include `data-block={props._type}` in the root element
return (
<div {...props.rootHtmlAttributes}>
<PropsDisplayer data={props} label="New Block (Example)" />
</div>
)
}
export default NewBlock
Sets-up this component as the block's renderer in blockComponents
/**
* Used by `BlocksRenderer` to render the apropriate component for each block in Sanity data.
*/
export const blockComponents: Record<string, React.ComponentType<any>> = {
// %CLI/INJECT-VARIABLE%
'block.newBlock': NewBlock,
// ...
}
Create a GROQ query fragment to fetch the block's data in queries/block.queries.ts
// @TODO: populate block's GROQ query fragment
/** @returns `NewBlockBlockData` in [block.types](../types/block.types.ts) */
const NEW_BLOCK = /* groq */ `
...,
`
Set-up this fragment as the way to fetch data for this block in BLOCKS_BODY_FRAGMENT
export const BLOCKS_BODY_FRAGMENT = `
...,
_type == "block" => {
${RICH_PARAGRAPH_FRAGMENT}
},
// ...
_type == "block.newBlock" => {
${NEW_BLOCK}
},
`
Create Typescript definitions for the block's data
By writing TS definitions of your block's data, you make it easier to write and maintain the React components that tap into it. By default, the block component (above) will use the block's data type.
// @TODO: populate block's data type definition
export interface NewBlockBlockData extends BlockInRenderer {
title?: string
}
What's next after the creation
- We leave a few
@TODO
comments in the files created. Follow them to complete the block's implementation. - Define the block's
variants
in its schema file with itstitle
andassetUrl
to guide editors when choosing blocks in the landing page builder