Building the First Page Builder Schema
This is the first step towards laying a solid foundation for more complex structures down the line.
Choices you make at this stage will determine the efficiency of content generation. Keeping your schema simple and well-structured is the key to effortless content operations.
Learning objectives
By the end of this lesson, you'll be able to:
- Structure a page builder
- Implement new blocks
- Understand when to use references vs objects
Basic block structure
Let's start by creating your first pageBuilder block. Note that this example uses an object type for creating the block. As Simeon explained eloquently:
If you use objects, the content is easier to query but trapped within the document.
If you use references, the content can be reused between documents, and your queries must resolve them.
This is a great way to think about it. Since you are new to page builders, the following example will start with objects, and the reasons for this choice will be explained later.
The next step is to add a splitImage block, a simple layout with text on one side and an image on the other, either left or right. You've definitely seen this block on a lot of websites.
Here's a link for what the block might look like (opens in a new tab)
import { defineField, defineType } from "sanity";
export default defineType({
name: "textAndImage",
type: "object",
fields: [
defineField({
name: "orientation",
type: "string",
options: {
list: ["imageLeft", "imageRight"],
},
}),
defineField({
name: "title",
type: "string",
}),
defineField({
name: "image",
type: "image",
}),
],
});
That's great, you have your first block.
Now let's add a hero block. This is a simple block that has a title text and image. Despite the schemas looking very similar, you would usually have a hero at the top of a page, so it's a good idea to have a dedicated block for it.
You may have noticed that the code snippets use a block field for the text. This is known as portable text, a powerful way to render rich text within Sanity. While it's more complex than a simple string, its flexibility makes it incredibly useful. For more details, check out the documentation here (opens in a new tab).
Here's a link for what the block might look like (opens in a new tab)
import { defineField, defineType } from "sanity";
export default defineType({
name: "hero",
type: "object",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "text",
type: "block",
}),
defineField({
name: "image",
type: "image",
}),
],
});
Building an FAQ block: References or Objects?
This is an ideal example for using references - to reuse the same document in multiple places.
If you have a list of FAQs that you want to show on multiple pages, you can create a single FAQ document and reference it from each page, rather than duplicating the FAQ content. This makes it easier to maintain since you only need to update the content in one place.
Let's create a FAQ document type and reference it in a block. First, let's create the FAQ document schema:
In this example, the name of the block is faqAccordion
Here's a link for what the block might look like (opens in a new tab)
first, create the document schema which will reference in your block.
import { defineField, defineType } from "sanity";
export default defineType({
name: "faq",
type: "document",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "body",
type: "block",
}),
],
preview: {
select: {
title: "title",
},
},
});
Second, create the block schema which will reference the FAQ document.
import { defineField, defineType } from "sanity";
export default defineType({
name: "faqAccordion",
type: "object",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "faqs",
type: "array",
of: [{ type: "reference", to: [{ type: "faq" }] }],
}),
],
preview: {
select: {
title: "title",
},
},
});
Finally, one more block. This one is a features block, this is going to get a little more complex with having an array of features (as a block) inside a block.
Here's a link for what the block might look like (opens in a new tab)
import { defineField, defineType } from "sanity";
export default defineType({
name: "features",
type: "object",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "features",
type: "array",
of: [
defineType({
name: "feature",
type: "object",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "text",
type: "string",
}),
],
}),
],
}),
],
});
Okay great, you have got your blocks. Now let's put them together in your page builder component. The order of the blocks is how it will appear when a user adds a new block to the array. In this example the hero block will appear first, then the splitImage block, then the features block.
import { defineField, defineType } from "sanity";
export default defineType({
name: "pageBuilder",
type: "array",
of: [
{ type: "hero" },
{ type: "splitImage" },
{ type: "features" },
{ type: "faqAccordion" },
],
});
Now that you have your page builder schema set up, all the fundamental building blocks are in place. Next, you can add new blocks, reorder them, and update the array as needed.