CoursesBuild landing pages with Next.jsCreate page builder schema types
Track
Work-ready Next.js

Build landing pages with Next.js

Lesson
2

Create page builder schema types

Setup the initial "blocks" of content and set the foundation of your page builder schema types.
Log in to mark your progress for each Lesson and Task
The schema types you'll add in this lesson follow on from those created in the Content-driven web application foundations course.

The choices you make at this stage will determine the efficiency of content creation. Keeping your schema simple and well-structured is the key to effortless authoring.

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

The page builder is typically an array of object or reference types that can be reordered. It's the container for all your building blocks. With Sanity, there are no pre-built blocks, but it's fast and easy to create what you need.

  • If you use objects, the content is simpler to query but trapped within the document.
  • If you use references, the content can be reused between documents, and your queries must resolve them.

Let's start by creating your first pageBuilder block. Note that this example uses an object type to create the block.

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 many websites.

Here's a link for what this block could look like

Create the splitImage block
src/sanity/schemaTypes/blocks/splitImageType.ts
import { defineField, defineType } from "sanity";
export const splitImageType = defineType({
name: "splitImage",
type: "object",
fields: [
defineField({
name: "orientation",
type: "string",
options: {
list: [
{ value: "imageLeft", title: "Image Left" },
{ value: "imageRight", title: "Image Right" },
],
},
}),
defineField({
name: "title",
type: "string",
}),
defineField({
name: "image",
type: "image",
}),
],
preview: {
select: {
title: "title",
media: "image",
},
prepare({ title, media }) {
return {
title,
subtitle: "Text and Image",
};
},
},
});

Now, let's add a hero block. This is a simple block with 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.

See Presenting Portable Text in the documentation for more details

Here's a link for what this block could look like

Create the hero block
src/sanity/schemaTypes/blocks/heroType.ts
import { defineField, defineType } from "sanity";
export const heroType = defineType({
name: "hero",
type: "object",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "text",
type: "blockContent",
}),
defineField({
name: "image",
type: "image",
}),
],
});

An "FAQ Block" is an ideal block for using references, allowing the same document to be reused 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 faqBlock

Here's a link for what this block could look like

Create the faq document schema type
src/sanity/schemaTypes/documents/faqType.ts
import { defineField, defineType } from "sanity";
export const faqType = defineType({
name: "faq",
title: "FAQ",
type: "document",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "body",
type: "blockContent",
}),
],
});
Create the faqAccordion block, which will reference the FAQ document.
src/sanity/schemaTypes/blocks/faqsType.ts
import { defineField, defineType } from "sanity";
export const faqsType = defineType({
name: "faqs",
title: "FAQs",
type: "object",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "faqs",
title: "FAQs",
type: "array",
of: [{ type: "reference", to: [{ type: "faq" }] }],
}),
],
});

Finally, one more block. This one is a features block, and this is going to get a little more complex with an array of features (as a block) inside a block.

Here's a link for what this block could look like

Create the features block
src/sanity/schemaTypes/blocks/featuresType.ts
import { defineField, defineType } from "sanity";
export const featuresType = defineType({
name: "features",
type: "object",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "features",
type: "array",
of: [
defineField({
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.

Create the pageBuilder schema type
src/sanity/schemaTypes/pageBuilderType.ts
import { defineType, defineArrayMember } from "sanity";
export const pageBuilderType = defineType({
name: "pageBuilder",
type: "array",
of: [
defineArrayMember({ type: "hero" }),
defineArrayMember({ type: "splitImage" }),
defineArrayMember({ type: "features" }),
defineArrayMember({ type: "faqs" }),
],
});

The schema types in your Sanity Studio for now are only useful for writing blog posts. The pages being built with this schema have a different intention to those, and so should be stored in a distinct schema type.

Create a page document schema type
src/sanity/schemaTypes/pageType.ts
import { DocumentIcon } from "@sanity/icons";
import { defineField, defineType } from "sanity";
export const pageType = defineType({
name: "page",
title: "Page",
type: "document",
icon: DocumentIcon,
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "slug",
type: "slug",
options: {
source: "title",
},
}),
defineField({
name: "content",
type: "pageBuilder",
}),
defineField({
name: "mainImage",
type: "image",
options: {
hotspot: true,
},
}),
],
preview: {
select: {
title: "title",
subtitle: "slug.current",
},
},
});

Finally, update the schema types index file to import all of these newly created schema types.

Update your registered schema types
src/sanity/schemaTypes/index.ts
// ...all your existing imports
import { pageType } from "./pageType";
import { pageBuilderType } from "./pageBuilderType";
import { faqType } from "./faqType";
import { faqsType } from "./blocks/faqsType";
import { featuresType } from "./blocks/featuresType";
import { heroType } from "./blocks/heroType";
import { splitImageType } from "./blocks/splitImageType";
export const schema: { types: SchemaTypeDefinition[] } = {
types: [
// ...all your existing schema types
pageType,
pageBuilderType,
faqType,
faqsType,
featuresType,
heroType,
splitImageType,
],
};

The desk structure will now include page and faq type documents, but won't display them nicely with plurals.

Update the Studio's structure configuration
src/sanity/structure.ts
import type { StructureResolver } from "sanity/structure";
// https://www.sanity.io/docs/structure-builder-cheat-sheet
export const structure: StructureResolver = (S) =>
S.list()
.title("Blog")
.items([
S.documentTypeListItem("post").title("Posts"),
S.documentTypeListItem("category").title("Categories"),
S.documentTypeListItem("author").title("Authors"),
S.divider(),
S.documentTypeListItem("page").title("Pages"),
S.documentTypeListItem("faq").title("FAQs"),
S.divider(),
...S.documentTypeListItems().filter(
(item) =>
item.getId() &&
!["post", "category", "author", "page", "faq"].includes(item.getId()!)
),
]);

With all of these new schema types registered you should now be able to create Page type documents with the Page Builder field allowing you to add any one of four blocks.

Now that your page builder schema is set up, all the fundamental building blocks are in place. Next, you can add new blocks, reorder them, and update the array as needed.

You have 9 uncompleted tasks in this lesson
0 of 9