Adding code blocks to Sanity.io Studio to improve blog
Adding code blocks to the blog
Today, as part of my #100DaysOfGatsby personal challenge, I'm going to enhance the portable text editor Sanity provides us in the starter blog. I especially want to add support for code blocks, which are a critical thing to have on a dev blog. Once I have the studio updated I will upgrade my Gatsby code to support it.
Update the studio
Let's start with the Studio. In the code Sanity provides as part of its blog starter, there is a different portable text setup for different sections. One for the body, one for the excerpt and one for the author bio. This is good strategy as you don't necessarily want a full editing block for the excerpt, where you do for the body of the blog. I'm fine with the limited set in the bio and excerpt, but I want to have about everything possible for the blog body, so that is where I'm going to focus.
Out of the box, I have Normal, H1 - H4, and quote for text styles. There are also Emphasis, Strong, bullet list, numbered list, html link, image and author reference. Here's what that looks like.
Sanity offers more annotations out of the box, however (code, underline and strike-through), so I'm going to add those immediately.
In my project under studio/schemas/objects there is a file called "bodyPortableTest.js". That's the one we need to edit.
Inside this file you will quickly see all the types listed broken out into sections:
- styles for text styles (H1, Quote, etc.)
- lists for the lists (unordered lists, numbered list)
- marks for other non-object things (decorators like strong, emphasis, underline, etc., annotations like link.)
- Lastly you can add custom objects to the end, which is where the image object is.
I am going to add some new decorations that are built in. The current decorations are:
decorators: [{title:'Strong', value:'strong'}, {title:'Emphasis', value:'em'}],
I will change this to add the other built in decorators so that this section will now look like the following:
decorators: [
{title: 'Strong', value: 'strong'},
{title: 'Emphasis', value: 'em'},
{title: "Code", value: "code" },
{title: "Underline", value: "underline" },
{title: "Strike", value: "strike-through" }
]
Now I have the following options:
This is an improvement, but we are still not there. Specifically with the code block. The out of the box option will change the text to a monospace font and add a different background color, which is an improvement. But it is not good for larger code blocks and does not have some of the extra nice features like a copy link, language display, and highlighting. Sanity has a plug-in built to use with their own docs, and give us the ability to use that as well, so I'm going to install it.
Start by installing the needed plugin.
sanity install @sanity/code-input
I'm going to leave the current code decorator as it might be useful but if you want to remove it, simply remove it from the decorator list we just used. It will now be:
decorators: [
{title: 'Strong', value: 'strong'},
{title: 'Emphasis', value: 'em'},
{title: "Underline", value: "underline" },
{title: "Strike", value: "strike-through" }
]
Now I need to add the code object, which will make the code block show up in the "insert" list. To do this, add the following after the mainImage block near the bottom:
{
type: 'code'
}
Now stop and restart the studio
sanity start
And now you should see your code block option under insert:
and be able to have a nice code editor plugin
Which will display nicely in your Studio
Upgrade Gatsby
The downside to adding a new portable text type, is that our frontend doesn't know how to deal with it and so we have broken the front end build. Let's go update our Gatsby code to teach it to understand this type and to know how to display it properly.
The Gatsby code is in the web folder, and the component that handles portable text is under the web/src/components folder and is predictably called portableText.js (You can look at the blogPost.js component to figure this out as well, just in case it is not completely obvious).
When you look at the portableText.js, you see there is not much happening. It's basically pulling in the config info from your Sanity config, and the sanity code from sanity to handle the block content, and calling a third component called "serializers" - this is the file we want.
The serializers.js file is where you define how the sanity block content handlers treat different blocks. The main thing you need to put here is how to handle any custom object you add. If you look at this one you will see there is already a mainImage and an authorReference defined (these are the 2 objects that are insertable in your sanity body block. To make my code block work, I need to add it here. I'm going to separate the code out to another component called code.js, so all I need to do here is add an import for ./code.js at the top of the file and then add a reference to the code block. Here is the resulting serializers.js
import React from 'react'
import Figure from './Figure'
import Code from './Code'
const serializers = {
types: {
authorReference: ({node}) => <span>{node.author.name}</span>,
mainImage: Figure,
code: Code
}
}
export default serializers
For the new code.js component, I need to handle the output of the code block. I know from looking at the docs that the code plugin object has a language and a code prop, so that is what I will use to create my output.
import React from "react";
export default ({ node }) => {
if (!node || !node.code) {
return null;
}
const { language, code } = node;
return (
<div>
language = {language}
<pre>
{code}
</pre>
</div>
);
};
Now my code is displayed a little nicer. But it's still not great. There is no syntax highlighting or much style at all. Fortunately, we are using react and there are a ton of react components that do exactly this. I choose react-syntax-highlighter because Knut did in his video where he walks through doing exactly what I'm doing here, and also because it was the first result in a google search. I may go back and research different options and choose something else later, but this works well for now. (If you want to see a quick video version of what I'm doing in this blog post I recommend Knut's video on YouTube - it goes fast but is very close to what I'm doing here, and was where I learned most of what I know of this topic.)
React-syntax-highlighter is the package I need. Change to the /web directory and install it with npm.
npm install react-syntax-highlighter --save
Then edit the serialzers.js to look something like this
import React from 'react'
import SyntaxHighlighter from 'react-syntax-highlighter';
export default ({node}) => {
if (!node || !node.code) { return null }
const {language, code} = node
return (
<SyntaxHighlighter language={language || 'text'}>
{code}
</SyntaxHighlighter>
)
}
You can read the docs on the component if you want to see all the options it has available and to make this look exactly to your taste. For me, this is perfect for now.
The end result is nice looking code blocks (that you see in action on this page already)
I think that is where I will stop. I'll likely revisit this in the future to add things like a copy button, code type, etc. but I want to move on to other things for now. I may want to choose a different component for that as well, and that will take some research.
Next time I will plan to further extend the portable text by adding a video embed, and to improve on the image and the url abilities.