Tailoring content for different audiences
How to deliver better content experiences across cultures, languages, and tastes.
Go to Tailoring content for different audiencesBuild a content model from scratch with Sanity.io. Create your first custom type and learn how to think about taxonomies along the way.
This guide continues our series on content modeling with Sanity.io. In the previous chapter, we introduced CandiCorp – Norway’s manufacturer of the world’s tastiest and most interesting treats (at least in a parallel universe). Before that we talked about what content modeling is, made a case for why it’s important, and explained how it brings a ton of value to projects of all kinds.
If you’ve been eager to build structure the wait is over! We’ll now setup the foundations CandiCorp’s content model.
Content models can take shape in a variety of ways. When you model content in Sanity – you’re not building a separate thing that you need to implement in code at a later time, you’re setting up:
If this sounds a bit daunting – it’s not as hard as you might think, and provides a lot of long term value because:
We spent a lot of time making content modeling in Sanity easy and intuitive; but you do need to know the basics of creating objects and referencing files in JavaScript.
If you’re reading this as a content designer or strategist who is somewhat familiar with web development, it shouldn’t be too hard to follow along. (We’d like to think that writing complex Google Sheets and Excel functions are harder than setting up a Sanity schema.)
If coding’s not your thing, team up with someone who for all the implementation stuff. Modeling as a content+dev team can be real-time, flexible, and very productive.
If you’re familiar with Sanity schemas, keep reading. If this is your first time, take a peek at the following first:
Create a new directory called candicorp
on your computer and move into it. Then initiate a new Sanity project using the following command in your shell application of choice (for example Terminal, or PowerShell):
~/candicorp
sanity init
Then set up an account with Sanity and login with Google, Github, or your own e-mail. Sanity will now download all the necessary stuff and in a few minutes, you should see a success message. Open your project folder in a code editor and you’ll see a structure like this:
├── README.md ├── config ├── node_modules ├── plugins ├── sanity-schema.txt ├── sanity.json ├── schemas // content model lives here ├── static ├── tsconfig.json └── yarn.lock
We’ll be doing all the modeling work in the schemas
folder.
Install version control like git in your project folder to track the way your content model changes over time.
Sanity Studio comes with a development server that will automatically refresh the studio in the browser when you make edits to the content model. You start the local development environment with this command:
sanity start
In a few moments you should receive a success message and the local URL of your Sanity Studio:
Sanity Studio successfully compiled! Go to http://localhost:3333
Head over to that URL in your browser and log in with the same credentials that you set up your Sanity account with. It will load the Sanity Studio environment:
The Empty Schema notice tells us that there are no document types set up, and this is exactly how we want it. Let’s do what the notice says and set up a new document type.
Sanity differs from other content management solutions in that it doesn’t lock you in to ideas about how your content should be structured. We believe your structure should be unique to your needs.
In the previous chapter, we figured out a basic mental model for CandiCorp and explained the reasons behind how they got there. Let’s revisit the diagram again:
We'll start by building out an article
content type. Within the schemas
folder of our project there is a file called schema.js
that comes with some handy comments on installation:
// schema.js
// First, we must import the schema creator
import createSchema from 'part:@sanity/base/schema-creator'
// Then import schema types from any plugins that might expose them
import schemaTypes from 'all:part:@sanity/base/schema-type'
// Then we give our schema to the builder and provide the result to Sanity
export default createSchema({
// We name our schema
name: 'default',
// Then proceed to concatenate our document type
// to the ones provided by any plugins that are installed
types: schemaTypes.concat([
/* Your types here! */
])
})
This file is where the entire content model comes together. We can add our own content types and fields at the bottom of that file. Modeling everything within schema.js
is great for small projects, but we can also import
references to other files and folders.
CandiCorp will have quite a few moving parts, so let’s set up a folder and the file-based way of handling the schema:
documents
in /schemas
.objects
in /schemas
(we’ll cover this in the next guide)article.js
within the /schemas/documents
. Our project structure should now look like this:
├── README.md ├── config ├── package.json ├── plugins ├── sanity-schema.txt ├── sanity.json ├── schemas │ ├── documents // new │ │ └── article.js // new │ ├── objects // new │ └── schema.js ├── static ├── tsconfig.json └── yarn.lock
Open article.js
and add a title
input field for the article, and make it a string field type:
// article.js
export default {
title: 'Article', // The human-readable label. Used in the studio.
name: 'article', // Required. The field name, and key in the data record.
type: 'document', // Required. The name of any valid schema type.
// Input fields below, as many as you need.
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
}
]
}
We’ve setup our first document and field, but we need to tell schema.js
that it exists so it can build the content type in Sanity Studio.
// schema.js
import createSchema from 'part:@sanity/base/schema-creator'
import schemaTypes from 'all:part:@sanity/base/schema-type'
import article from './documents/article' // new
export default createSchema({
name: 'default',
types: schemaTypes.concat([
article, // new
])
})
You can review the complete list of schema types in our documentation.
Save the article.js
and schema.js
files. Your studio environment should now have a new artilcle ocument type that you can start to work with!
Let‘s make a category
taxonomy for articles and other things. There are a few ways to do this with Sanity. What you choose depends on what you need:
We could make categories an array of strings that editors can add to each article, like so:
// article.js
export default {
title: 'Article',
name: 'article',
type: 'document',
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
},
{
title: 'Categories', // new
name: 'categories',
type: 'array',
of: [{type: 'string'}],
options: {
layout: 'tags',
}
},
]
}
This approach is often used in “tag clouds” which connect a layer of meta content to the main content type. It's super flexible because the editor can add any value to the string field, and any number of strings to the array.
But its flexibility is also its downside: all that freedom makes it possible for editors to create a lot of ambiguity due to a lack of constraint. There’s also no single source of truth that binds the category
values between article
records, and the only way to know what’s in use is to query all the values for all records.
To manage candy categories centrally we could make the string array contain fixed values by adding a list of options:
// article.js
export default {
title: 'Article',
name: 'article',
type: 'document',
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
},
{
title: 'Categories',
name: 'categories',
type: 'array',
of: [{type: 'string'}],
options: {
list: [ // these values will be the only available options
{value: 'hard-candy', title: 'Hard Candy'},
{value: 'soft-candy', title: 'Soft Candy'},
// etc
],
layout: 'radio' // <-- defaults to 'dropdown' with a list of values
},
},
]
}
This solves the problem of ambiguity because the category values are set in stone. It's useful if your taxonomy is well established and unlikely to change a lot over time.
But (and there’s usually always a “but” when it comes to content modeling) because these values are set in code they’re not easy for editors to manage. If that’s important you may be better served by references.
To reuse a fixed list like this in other content types, place that field in an object type, so you can include it in any other object or document.
We can strike a balance between flexibility and single-sourcing by creating a new content type file of category.js
, and then establish a reference between it, and article.js
.
Let's create the file:
// category.js
export default {
title: 'Category',
name: 'category',
type: 'document',
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
},
// add a unique slug field for queries, permalinks etc
{
title: 'Slug',
name: 'slug',
type: 'slug',
options: {
// auto generates a slug from the title field
source: 'title',
auto: true
}
}
]
}
Put it in the schema folder:
├── schemas │ ├── documents │ │ └── article.js │ │ └── category.js // new │ ├── objects │ └── schema.js
And add it to schema.js
:
// schema.js
import createSchema from 'part:@sanity/base/schema-creator'
import schemaTypes from 'all:part:@sanity/base/schema-type'
import category from './documents/category' // new
import article from './documents/article'
export default createSchema({
name: 'default',
types: schemaTypes.concat([
article,
category, // new
])
})
And relate the two with an array of references:
// article.js
export default {
title: 'Article',
name: 'article',
type: 'document',
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
},
{
title: 'Slug',
name: 'slug',
type: 'slug',
options: {
source: 'title',
}
},
{
title: 'Categories',
name: 'category',
type: 'array',
of: [
{
type: 'reference',
to: [
{type: 'category'},
// etc
]
}
]
},
]
}
Now we can create as many categories as we like, edit them in Sanity Studio, and connect to articles, and anything else for that matter. We can also extend category.js
at a later time. This might come in handy for SEO category pages, or as section headings in CandiCorp’s print catalog.
When adding references, consider the editing experience when deciding where you place them.
Referencing categories within article.js
asks the editor to think about how their articles should be categorized while they’re thinking about article content.
You could also do the opposite and reference an array of articles within category.js
– asking editors to think of which articles best match the category.
Sometimes you need a tightly curated list of references, and other times you need infinite references. Each one is useful and leads to different results.
CandiCorp decided to create a new category.js
document type and reference categories within article.js
because:
We figured out how to bootstrap a content model in Sanity Studio from a blank canvas. We then created our first custom content type and provided it with a taxonomy for organisation.
Along the way, we explored different ways of handling taxonomies in Sanity.io, and talked about the importance of putting references in the right place depending on what we want to achieve.
We’ll build out all of CandiCorp’s main fields and relationships. We’ll work through a range of juicy content modelling dilemmas that come with the terrain.
How to deliver better content experiences across cultures, languages, and tastes.
Go to Tailoring content for different audiencesHierarchies are handy for organizing, but they can also fence you in. Learn how to build them, when to use them, and why you might want to treat navigation as a separate concern.
Go to Hierarchies, Graphs, and NavigationWhat you need to know when starting a new content model
Go to 8 tips for better content modelingLearn how to shape Sanity Studio to improve editor flows and content quality.
Go to How to create an effective editor experience