How to build an input component for Sanity Studio v3
This guide teaches how to add a custom input component to a field for Sanity Studio v3
Go to How to build an input component for Sanity Studio v3You can use structured content to make landing page builders that will be useful beyond your next redesign. This guide shows you the basics of page building, and offers advice for dealing with presentation-related concerns.
Sanity can be used to manage things like landing page builders: they give editors enough control over page composition to get their message across using content modules, without breaking layout.
In this guide, you’ll find suggestions for how to create content modules for page builders that should nicely translate to a component-based frontend framework or design system.
While page builders can be a very handy approach to content creation, it's worth asking yourself if a page builder is what you actually need. You can also arrive at compelling combinations of content and presentation by sourcing content from from various places using simple rules in your frontend.
The goal of structured content is to make sure that your content stays resilient, adaptable, and easy to integrate wherever you need it. That’s why you should generally make content models that reflect what your content means rather than how it is presented. Because different presentation contexts (even within the same medium) come with different constraints: what makes sense on the web might not make sense in an app, and so on.
This guide makes no assumptions about presentation: no colors, floats, etc. While it might be tempting to add these, we think it best to leave those kinds of concerns to your code. They can add complexity not just to the implementation, but also to the things editors need to keep track of.
Think about your next redesign. Would you rather:
We find that modeling for meaning leads to better workflows and more durable content.
The rest of this guide involves a basic knowledge of schema building with Sanity.io. If you’ve never made one before, take a 3 minute detour to learn the basics of schema configuration, and/or keep our schema docs open as a reference .
The page builder is an array of custom types that can be reordered. It's the container for all your building blocks. With Sanity there are no pre-built blocks for you to use, but its fast and easy to make what you need.
Let's add some blocks you’d expect to see on a typical landing page:
Now let's bring them to life in a bare-bones document type called page
:
// page.js
export default {
// Setup a 'document' type to house the page builder field
name: "page",
type: "document",
title: "Page",
fields: [
{
name: 'pageBuilder',
type: 'array',
title: 'Page builder',
of: [
{ type: 'hero' }, // hero.js (same applies for the other types)
{ type: 'textWithIllustration' },
{ type: 'callToAction' },
{ type: 'gallery' },
{ type: 'form' },
{ type: 'video' },
// etc...
]
}
]
}
Our pageBuilder
field consists of a name
, title
, and the field type of array
. Within the array, we list all the types that we want to build with. Now that we’ve declared our types, let's create the accompanying schema files.
Let's setup hero.js
as an object type so that can be reused elsewhere in our schema if we need it. We’ll add fields for heading
, tagline
, and an image
.
// hero.js
export default {
name: "hero",
type: "object",
title: "Hero",
fields: [
{
name: 'heading',
type: 'string',
title: 'Heading'
},
{
name: 'tagline',
type: 'string',
title: 'Tagline'
},
{
name: 'image',
type: 'image',
title: 'Image',
options: {
hotspot: true,
},
fields: [
{
name: 'alt',
type: 'string',
title: 'Alternative text'
}
]
}
]
}
In the image field, we enabled the hotspot option for image art direction and added a simple string field for alternative text. Alt-text provides a text-based alternative to non-text content (like images) on web pages. Among other things, it helps vision-impaired people understand the meaning of your images.
If providing meaningful alt-text descriptions is really important to you, make it mandatory by applying validation to this field.
Those fields will look like this in your Sanity Studio:
The most simple form of a call-to-action is a URL and a string field which we can use for the link text. So, let's set that up in a new object:
// callToAction.js
export default {
name: "callToAction",
type: "object",
title: "Call to Action",
fields: [
{
name: 'linkText',
type: 'string',
title: 'Link Text'
},
{
name: 'url',
type: 'url',
title: 'URL'
}
]
}
This object looks a lot like our hero except we’ve added a field called excerpt
to store multiline text content.
// textWithIllustration.js
export default {
name: "textWithIllustration",
type: "object",
title: "Text with Illustration",
fields: [
{
name: 'heading',
type: 'string',
title: 'Heading'
},
{
name: 'tagline',
type: 'string',
title: 'Tagline'
},
{
name: 'excerpt',
type: 'text',
title: 'Excerpt'
},
{
name: 'image',
type: 'image',
title: 'Image',
options: {
hotspot: true,
},
fields: [
{
name: 'alt',
type: 'string',
title: 'Alternative text'
}
]
}
]
}
If you need more than plain text you could use the block content type to include things like bold, italics, etc.
When you strip away all the presentation concerns, a gallery is just a sortable list of images. Normally the array type presents a vertically draggable list, but if you set it to grid
it will do look like the example above. Here's how you do it:
// imageGallery.js
export default {
name: "gallery",
type: "object",
title: "Gallery",
fields: [
{
name: 'images',
type: 'array',
title: 'Images',
of: [
{
name: 'image',
type: 'image',
title: 'Image',
options: {
hotspot: true,
},
fields: [
{
name: 'alt',
type: 'string',
title: 'Alternative text'
}
]
}
],
options: {
layout: 'grid'
}
}
]
}
Forms come in many different shapes and sizes. In order to preserve the durability of our content structure beyond the next redesign, all we really need to do is declare the kind of form we want to embed in our page builder array. Here's an example presenting 3 variations for newsletter
, register
, and contact
form types:
// form.js
export default {
name: "form",
type: "object",
title: "Form",
fields: [
{
name: 'label',
type: 'string',
title: 'Label'
},
{
name: 'heading',
type: 'string',
title: 'Heading'
},
{
name: 'form',
type: 'string',
title: 'Form',
description: 'Select form type',
options: {
list: ['newsletter', 'register', 'contact']
}
}
]
}
You can then use frontend code to provide varying presentations of your forms depending on the page context, and the type of form you selected.
If you strip away presentation-based thinking, a video object can be modeled in the same way as our call to action object:
string
field for the video's label// video.js
export default {
name: "video",
type: "object",
title: "Video",
fields: [
{
name: 'videoLabel',
type: 'string',
title: 'Video Label'
},
{
name: 'url',
type: 'string',
title: 'URL'
},
]
}
Because we avoided embedding presentation concerns in our page builder we now have the flexibility to present that content in any number of ways in frontend code. Here are a few style variations applied to the same 3 fields in our hero content block. It's possible to present those fields in countless numbers of ways without compromising the meaning of our content.
We've learned the basics of modeling a page builder with Sanity.io. We've primed the pump with a few common builder modules that you can alter or extended to fulfill the unique needs of your project.
Along the way, we made a case for keeping presentation-related concerns out of your content models. Content editing will be less complicated, and code maintenance will be easier, and your next redesign budget will thank you for it!
If you’d like to test out page building with Sanity give our Kitchen Sink starter project a try. It includes real-time content previews and a host of other goodies.
This guide teaches how to add a custom input component to a field for Sanity Studio v3
Go to How to build an input component for Sanity Studio v3A thorough intro to using GROQ-projections in a webhook contest
Go to GROQ-Powered Webhooks – Intro to ProjectionsA thorough intro to using GROQ-filters in a webhook-context
Go to GROQ-Powered Webhooks – Intro to FiltersLearn how to implement live content previews with Next.js and Sanity Studio
Go to Live Preview with Next.js and Sanity Studio: A Complete Guide