Providing content editors with the ability to apply rich text formatting, be it in long-form content with styled headings, or providing emphasis in places of particular importance can greatly enhance a user’s reading experience. Sanity Studio supports many different formatting methods using their editor interface and within this guide we’ll explain how to ensure those same formatting rules are applied when the data is displayed on a web page.
#TL;DR
In a previous guide we built a simple Astro site that uses Sanity as the CMS.
Within this guide, you’ll discover how to use Portable Text to enable rich text formatting and the process for embedding images within the content body.
You can see a preview of the deployed site and the repository for the code on the following links.
- Preview: https://example-astro-sanity-portable-text.netlify.app/
- Repository: https://github.com/netlify/example-astro-sanity-portable-text
#Prerequisites
These are the things you’ll need to set up before starting this tutorial:
- Complete all steps outlined in the first part of our Sanity and Astro guide
#What is Portable Text?
Portable Text is a JSON based rich text specification for modern content editing platforms. Portable text is an agnostic abstraction of rich text that can be serialized into pretty much any markup language, be it HTML, Markdown, SSML, XML, etc.
Portable Text is built on the idea of rich text as an array of blocks. Each block can have a style and a set of mark definitions, which describe how the data could be presented. Portable Text also allows for custom content objects, enabling editing and rendering environments to mix rich text with custom content types.
Here’s an example.
#Install dependencies
There two dependencies required to enable Portable Text and embedded images are astro-portabletext and @sanity/image-url. You’ll need both installed before proceeding.
Run the following in your terminal.
#Update Sanity Schema
Open sanity.config.ts
and add the following types to the schema.
The new content is of type blockContent
. Within this object we can use the of type to create an array of the two new content types. The first type is block
, the second is image
.
#block
The block type schema is where you can define the fields that will appear in Sanity Studio and will allow editors to create headings, blockquotes, lists, marks for bold and italic text and annotations such as links.
You can read more about the available bocks in the Sanity docs: Usage.
The image type is for images that can be added to the body of a blog post, we’ve also defined a field for the alt text which will be the text added to the alt attribute on the HTML img element.
#image
If you’ve added both of the above, head back over to /admin
and you should see a new field in one of your posts where you can format headings, and add an image etc. Be sure to click publish to save your changes!
You can read more about images in the Sanity docs: Presenting Images.
#Rendering Portable Text
Open src/pages/posts/[slug].astro
and make the following changes.
The main changes here are that we’re now destructuring blockContent
from the sanityClient
query and passing it onto the <PortableText />
component via the value
prop.
In order to ensure the new Portable Text values within blockContent
can be correctly converted into HTML we’re passing a components object onto the <PortableText />
via the components
prop. It’s here where we can define which components map to which content schema types and ensure HTML elements are used.
You’ll notice in the components
object we’ve defined a key for list
. This key maps to a helpful Sanity component named List
.
#List Component
Sanity’s List component can determine which HTML list type, ol
(ordered list) or ul
(unordered list) to use based on the listItem
type that is present within the response.
You can also provide your own components.
Here’s an example of a custom list component which uses the listItem
type to determine which HTML list element is returned.
The usage in the components
object would remain the same. E.g
#Rendering Images
For images there’s a similar setup, but this time we will be using a custom image component.
The custom image component contains a number of other Sanity helpers to convert the image _ref
into an image url that can be used as the src
on an HTML img
element.
#Create an Image component
Create a new file named image.astro in src/components and add the following code.
This component has access to the asset _ref
which looks something like this: image-1085765fe253da8ca898a06fce40c049fd6f63e2-1024x576-jpg
.
By passing the _ref
to Sanity’s imageUrlBuilder
helper function it can construct the correct path to the image asset based on your projectId
and dataset
.
Add console.log(src)
to see the final constructed url. In our example it looks something like this:
https://cdn.sanity.io/images/hvpty17w/production/1085765fe253da8ca898a06fce40c049fd6f63e2-1024x576.jpg?w=512&fit=max&auto=format
The additional builder methods are to help with image formatting. You can read more about those in the Sanity docs: Builder methods.
#How to add custom components
In this next part we’ll explain how to add custom components to the content schema. In our example we’ve added a CTA (Call to action) which has two fields. Text, which is the text to display, and URL, which is the url to open in a new browser window.
The HTML anchor element is wrapped within a div which can be styled to suit your needs.
#Add custom schema
Open src/pages/posts/[slug].astro
and make the following changes below the image schema type.
If you head back over to Sanity Studio (/admin
) you should see the following CTA button has been added to the editor toolbar.
Clicking this will open a new modal where you can add the Text and URL for the call to action.
#Finished
And that’s it, you’re now able to add rich tech formatting and images to the body of your content, as well as being able to create custom components. Congrats!