Custom PageBuilder Input Component - Preview Blocks & Sections
Wanna use Sanity to build a PageBuilder? Show previews of your blocks/sections!
PageBuilderInput.tsx
"use client";
import { ArrayOfObjectsInputProps, BooleanSchemaType, FileSchemaType, NumberSchemaType, ObjectSchemaType, ReferenceSchemaType, StringSchemaType } from "sanity";
import {
Grid,
Stack,
Button,
Dialog,
Box,
Card,
Heading,
} from "@sanity/ui";
import { useCallback, useState } from "react";
import { AddIcon } from "@sanity/icons";
import { randomKey } from "@sanity/util/content";
import React from "react";
type Schema = BooleanSchemaType | FileSchemaType | NumberSchemaType | ObjectSchemaType | StringSchemaType | ReferenceSchemaType;
const PageBuilderInput = (props: ArrayOfObjectsInputProps) => {
const { onInsert } = props;
const [open, setOpen] = useState(false);
const onClose = useCallback(() => setOpen(false), []);
const onOpen = useCallback(() => setOpen(true), []);
const onSelectItem = useCallback((schema: Schema) => {
const key = randomKey(12);
onInsert({
items: [
{
_type: schema.name,
_key: key,
} as any,
],
position: "after",
referenceItem: -1,
open: true,
});
onClose();
}, []);
return (
<>
<Stack space={3}>
{props.renderDefault({
...props,
arrayFunctions: () => {
return (
<Button
onClick={onOpen}
icon={AddIcon}
mode="ghost"
text="Add item"
/>
);
},
})}
</Stack>
{open && (
<Dialog
header="Select a section"
id="dialog-example"
width={4}
onClose={onClose}
zOffset={1000}
>
<Box padding={1}>
<Grid autoCols={'auto'} columns={[1, 2, 2, 3, 4]} autoFlow={'row dense'} gap={[3]} padding={4}>
{props.schemaType.of.map((schema, index) => {
return (
<PreviewCard
key={index}
schema={schema}
onClick={() => onSelectItem(schema)} />
);
})}
</Grid>
</Box>
</Dialog>
)}
</>
);
}
type PreviewProps = {
onClick: React.MouseEventHandler<HTMLDivElement> | undefined,
schema: Schema
}
function PreviewCard(props: PreviewProps) {
const { onClick, schema } = props;
return (
<Card
role="button"
shadow={1}
padding={3}
onClick={onClick}
style={{ cursor: "pointer" }}
>
<Stack padding={2} space={[3]}>
<Heading as="h5" size={1}>
{schema.title}
</Heading>
<div
style={{
height: "150px",
}}
>
<img
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
src={`/sanity/${schema.name}.png`}
alt={schema.title}
onError={(i: any) =>
(i.target.style.display = "none")
}
/>
</div>
</Stack>
</Card>
)
}
export default PageBuilderInput;PageBuilder Array
defineField({
title: "Pagebuilder",
name: "sections",
type: "array",
group: 'pagebuilder',
of: [
{ type: homeHeroSection },
...
],
components: {
input: PageBuilderInput,
}
}),Do you want to use Sanity.io by creating some sort of Page Builder? But are you stuck with the simply array input and selecting a name like: "Block text with image on the right side"?
Create a more beautiful block/section selector with preview images!
How to use.
- First of all, you will need to copy the PageBuilderInput.tsx into your Sanity.io project files.
- Create a folder in your project which will contain all of your preview images (one per 'Block') and name your images after your schemaType. e.g. if your schemaType is
introWithTextyour image filename is introWithText.png. - Change the image src to your desired folder
src={`/sanity/${schema.name}.png`} - Use the component in your page builder array!
Contributor
studiodrimmel
Independent front-end developer from The Netherlands