Sanity Exchange: Community Appreciation Program
Overview of Sanity's Community Appreciation Program
Go to Sanity Exchange: Community Appreciation ProgramWhen using Sanity, it's important to understand Portable Text and what it is doing for your content. In this guide, we'll go over the 3 things to understand when getting started with Portable Text.
We have started a channel for beginners in the Sanity Slack Community, #getting-started. This channel is meant for those coming into the Sanity Community to feel safe to ask beginner Sanity questions or find past questions that others came across. We want to make sure that everyone feels supported, no matter what stage you are at with Sanity. Recently, we got a good list of questions in that channel about Portable Text.
I'll be honest, Portable Text didn't come easy to me since it represents a new and different way to approach rich text. But I will say, I think I've got a good hold on it. So I'm going to take you with me on my Portable Text journey and share with you what I found are 3 important things to know when getting started with Portable Text:
Before you jump into the 3 things to know when getting started with Portable Text, let's cover what Portable Text is! You may be familiar with using HTML or Markdown when using another CMS. At Sanity, we use Portable Text. So when you query for your content, your rich text content is returned as Portable Text.
When you restrict your content to HTML or Markdown only, you restrict it from the numerous platforms or types of software where you can send it. When using Portable Text, you can reuse your content across the web. It was built to be used across any format or platform. Need it to go to a browser with Vue.js? Or to a mobile app with React Native? Or spoken by a voice assistant? It can be written once in the Sanity Studio and reused across different platforms, frameworks, and anywhere you need your content to go!
Portable Text is broken into blocks of content, an array of objects, to be more exact. Imagine a blog post with a paragraph, an image, a sub-heading, another paragraph, and a YouTube embed. That would be considered 5 blocks of content. Portable Text is a JSON-based rich text specification. And we can see an example of that JSON format below. This format is meant to be easily readable for computers, reusable, and queriable. People interact with it by working in the Portable Text editor for Sanity Studio.
Here's a small example of three blocks of content. Notice the array within the "body" is separated into three blocks as an array of objects with some specific and recurring keys:
{
"_id": "drafts.6ae3ac6f-f033-403f-ad7a-65920daf8a6f",
"_rev": "66f32k-seu-0dv-rmo-44vyj9oxq",
"_type": "post",
"_updatedAt": "2021-05-20T20:02:29.542Z",
"body": [
{
"_type": "block",
"_key": "e5f8351bb4dd",
"style": "normal",
"markDefs": [],
"children": [
{
"_type": "span",
"_key": "d06d05d1ed5e",
"text": "This is block #1.",
"marks": []
}
]
},
{
"_type": "block",
"_key": "cf12b1566c13",
"style": "normal",
"markDefs": [],
"children": [
{
"_type": "span",
"_key": "feb044998e9c",
"text": "Block #2 is after that.",
"marks": []
}
]
},
{
"_type": "block",
"_key": "643bf733b859",
"style": "normal",
"markDefs": [],
"children": [
{
"_type": "span",
"_key": "6b8cc1d52738",
"text": "But block #3 is the end of our content.",
"marks": []
}
]
}
]
}
As you can see, all of these objects have the "_type": "block"
. If we had custom blocks, like a YouTube embed, it would probably say "_type": "youTube"
.
A mark in Portable Text is how we mark up inline text in the editor within the Sanity Studio. This can include marking text as strong or emphasized, as a code string, highlighted, or linking to an internal/external resource.
In the code block below, you see how Portable Text looks in its JSON format. Let's work through each line!
Note: This is different than the code block above. That code block had three blocks; this is one block broken down even further.
{
"_key": "9d2d1ed68d84",
"_type": "block", // the schema type for the Portable Text
"children": [
{
"_type": "span",
"marks": [],
"text": "I am "
},
{
"_type": "span",
"marks": [
"strong"
],
"text": "strong and "
},
{
"_type": "span",
"marks": [
"strong",
"cbe9d12c6af9" // notice this matches the "_key" below.
],
"text": "annotated"
},
{
"_type": "span",
"marks": [],
"text": "."
}
],
"markDefs": [
{
"_key": "cbe9d12c6af9", // notice this matches the "marks" above.
"_type": "link",
"href": "https://www.sanity.io/"
}
],
"style": "normal"
}
"children"
- the sections within our Portable Text block."_type"
- We'll see two layers of _type
. The first one: "_type": "block"
is stating that this a text paragraph. There may be many different blocks of content in your project. It's typically this type of block that has "children". The objects in the "children" array are where our text and marks live. This "_type"
is called a "span"
."marks"
- There is where we add some metadata about a span of text. We'll look at the second "span"
. This one has "strong"
meaning that whatever text is within this "span"
, it will typically be presented with a bold typeface."text"
- This is the text. The content is broken up into spans that you can customize further. If you look at the second "span"
, that "strong"
in the "marks"
array is going to bolden the text string: "strong and "
."strong"
and "cbe9d12c6af9"
on the third one. That means the text "annotated"
will be bold and linked. The seemingly arbitrary combination of letters and numbers is a reference to an object that you'll find within markDefs
"markDefs"
- You'll find the string "cbe9d12c6af9"
in the third span and within the "markDefs"
array. In other words, you can mark text with more complex data, expressed as an object with keys and values. It can be whatever you want, but here, it's used to express a link
. It has a field called "href"
that's used for storing a URL.Now, how does this all look in the end? That's up to you! You can “serialize” these blocks of content into a more readable format for putting it on the web, an app, or somewhere else (more on "serializers" below). If you serialize it into HTML, this JSON of content will look like this: I am strong and annotated
This code block and more information on "span" can be found here.
Marks can be either a decorator
or annotation
. Let's look at what each one means and what the difference is between them.
Now that you have broken down the block of Portable Text and what the numerous "marks" mean. Let's take that a little further. What if you want to customize the rich text editor and add different ways of marking up text?
Let's say we want to add the following decorators:
That code would look like this:
export default {
name: 'content',
title: 'Content',
type: 'array',
of: [
{
type: 'block',
marks: {
decorators: [
{ title: 'Strong', value: 'strong' },
{ title: 'Emphasis', value: 'em' },
{ title: 'Code', value: 'code' },
{ title: 'Highlight', value: 'highlight' }
]
}
}
]
}
This code would look like this in the rich text editor in the Sanity Studio:
As you are typing into the rich text editor, you can highlight certain words, click on one of these in the menu, and add that styling. Say you added strong
to some text that says, "I am bold". Once that styling is added to your text within your rich text editor, you can view your JSON version of your Portable Text and see that it would look like this:
{
"_type": "span",
"marks": [
"strong"
],
"text": "I am bold"
}
You can customize this however you need to within your rich text editor. Learn more about customizing the editor here.
An annotation within Portable Text is when you add an object of keys and values to your text. You are marking your text with a data structure. An example of this would be when you add a URL to the text. Let's look at how that code would look:
"body": [
{
"_type": "block",
"_key": "7de5c159e44d",
"style": "normal",
"markDefs": [
{
"_type": "link",
"_key": "8bd7459d6d4d",
"href": "https://www.sanity.io/"
}
],
"children": [
{
"_type": "span",
"_key": "c4323f8f316d",
"text": "Make this clickable to ",
"marks": []
},
{
"_type": "span",
"_key": "46299a39782f",
"text": "Sanity.io",
"marks": [
"8bd7459d6d4d"
]
}
]
In the toolbar, you can see the different marks
you can apply to your text. To add the annotation
, we would highlight the text we wanted to have the external URL. That would turn into this:
Some other examples of what an annotation
could be:
person
documents within your Content Lake (or anything else).Marks
are extra information that can be applied to your text. Ultimately, marks
can be broken into two sections:
In the below code, "strong"
is the decorator and "<markDefId>"
is the annotation of it.
"marks": []
// or
"marks": [
"strong",
"<markDefId>"
]
To learn more about these two, decorator
and annotation
, you can visit this docs page on customization.
A serializer is when the blocks of content from the JSON array are stacked together to make a readable format for where we want to present it. Think of it as a car factory where a bunch of parts is brought in, and it goes through a machine that puts the parts together and sends out a completed, fully assembled car with all the parts assembled correctly.
In our example above, our output was "I am strong and annotated." This is because the four spans
went through a serializer.
Default serializers can be found for multiple formats. These libraries come with some default serializers that will translate some common data structures to something that renders:
Let's look at the React method. Take a look at this code, and we'll walk through it together:
import {PortableText} from '@portabletext/react'
<PortableText
value={[/* array of portable text blocks */]}
components={/* optional object of custom components to use */}
/>
This is how it would look inside of a React component.
First, we will need to install the package: @portabletext/react
using the command:
npm install --save @portabletext/react
Next, at the top of the file, we need to import PortableText
:
import {PortableText} from '@portabletext/react'
Within the return
of the component, we'll insert the <PortableText/>
code like this:
<PortableText
value={[/* array of portable text blocks */]}
components={/* optional object of custom components to use */}
/>
This will take our post.body
content that we query from our Sanity Studio using GROQ or GraphQL. When our frontend grabs this part of the code, it will grab the blocks
of Portable Text, serialize it, and show it in a readable format.
In the above example, we are using no custom components. The components
object is blank so far. Sometimes we may want to customize our components beyond what the default components provide. For example, you may want to add some custom CSS styling to your links.
@portabletext/react provides a components
prop to allow us to pass in custom components.
We can begin to customize a link component by creating a custom components object and passing it to <PortableText components={myCustomComponents} />
This would be the beginning of us customizing our link component for React:
const myCustomComponents = {
marks: {
link: ({ children, value }) => {
return (
<a href={value.href} className="myCustomLinkClass">
{children}
</a>
)
},
},
}
const MyComponent = (props) => {
return (
<PortableText
value={props.value}
components={myCustomComponents}
/>
)
}
These are the 3 essential parts when getting started with Portable Text. From here, there are many things you can do with Portable Text.
Some of those include:
If you would like to share some of the content you create related to Portable Text or any Sanity content, feel free to share it in the Sanity Exchange Community. You can share guides, plugins, snippets, and projects for others to learn and grow from.
Happy coding, everyone! Thank you for reading.
Sanity Composable Content Cloud is the headless CMS that gives you (and your team) a content backend to drive websites and applications with modern tooling. It offers a real-time editing environment for content creators that’s easy to configure but designed to be customized with JavaScript and React when needed. With the hosted document store, you query content freely and easily integrate with any framework or data source to distribute and enrich content.
Sanity scales from weekend projects to enterprise needs and is used by companies like Puma, AT&T, Burger King, Tata, and Figma.
Overview of Sanity's Community Appreciation Program
Go to Sanity Exchange: Community Appreciation ProgramBuild a blog in React from scratch! We'll use Sanity for the content management and Tailwind CSS for styling.
Go to Build your first blog using React