Add the plugin, the schema, the components and PortableText rendering.
I mostly like Sanity.io as a CMS, but it’s changed rapidly over the past few years and there’s a fair amount of conflicting or old documentation and discussion around it. So here’s another guide to adding a code block to the Sanity Studio blockContent editor as of April 2024.
The outline:
- Install the @sanity/code-input plugin and add it to sanity.config.ts
- Add a code block to the blockContent schema
- Install react-syntax-highlighter and @types/react-syntax-highligher
- Create the component to display the code block
- Add the component to PortableText components
Install the plugin
1npm i @sanity/code-input
Import the plugin into sanity.config.ts and call it in the plugins array which would look something like this:
1import {visionTool} from '@sanity/vision'
2import {defineConfig} from 'sanity'
3import { structureTool } from 'sanity/structure'
4import { codeInput } from '@sanity/code-input'
5import {apiVersion, dataset, projectId} from './sanity/env'
6import {schema} from './sanity/schema'
7
8export default defineConfig({
9 basePath: '/admin',
10 projectId,
11 dataset,
12 plugins: [
13 structureTool(),
14 visionTool({ defaultApiVersion: apiVersion }),
15 codeInput(),
16 ],
17})
Add a code block to the blockContent schema, by adding a new defineArrayMember() with just the type equal to code. The name and title will be added by the plugin by default to be ‘Code’.
1import { defineType, defineArrayMember } from 'sanity'
2
3export default defineType({
4 title: 'Block Content',
5 name: 'blockContent',
6 type: 'array',
7 of: [
8 defineArrayMember({
9 title: 'Block',
10 type: 'block',
11 styles: [
12 { title: 'Normal', value: 'normal' },
13 { title: 'H1', value: 'h1' },
14 { title: 'H2', value: 'h2' },
15 { title: 'H3', value: 'h3' },
16 { title: 'H4', value: 'h4' },
17 { title: 'Quote', value: 'blockquote' },
18 { title: 'Caption', value: 'figcaption' },
19 ],
20 lists: [{ title: 'Bullet', value: 'bullet' }],
21 marks: {
22 decorators: [
23 { title: 'Strong', value: 'strong' },
24 { title: 'Emphasis', value: 'em' },
25 {
26 title: 'Caption',
27 value: 'caption',
28 icon: () => 'CAP'
29 },
30 {
31 title: 'Inline Code',
32 value: 'inlineCode',
33 icon: () => '</>',
34 },
35 ],
36 annotations: [
37 {
38 title: 'URL',
39 name: 'link',
40 type: 'object',
41 fields: [
42 {
43 title: 'URL',
44 name: 'href',
45 type: 'url',
46 },
47 ],
48 },
49 ],
50 },
51 }),
52 defineArrayMember({
53 type: 'image',
54 options: { hotspot: true },
55 fields: [
56 {
57 name: 'alt',
58 type: 'string',
59 title: 'Alternative Text',
60 },
61 {
62 name: 'height',
63 type: 'number',
64 title: 'Height',
65 },
66 {
67 name: 'width',
68 type: 'number',
69 title: 'Width',
70 },
71 ],
72 }),
73 defineArrayMember({
74 type: 'code',
75 }),
76 ],
77})
In the Sanity Studio editor, you should now see a Code item added the available blocks on the right side of the menu which opens a code block editor. In the editor, you select the language and paste in your code:
Sanity Studio block content editor
Install react-syntax-highlighter
1npm i react-syntax-highlighter
2npm i --save-dev @types/react-syntax-highlighter
Use the highlighter to create a code block component
Import the highlighter and the theme you want to use. react-syntax-highlighter comes with it’s own themes and any prismjs theme out of the box. You can swap the hljs at the end of the import with prismjs to use prismjs themes instead. Use the SyntaxHighlighter props including customStyle to quickly override any theme styles. The component will be passed an object with the programming language name and code from the API call for content. The SyntaxHighlighter has a prop for language and the code goes in as children.
1import SyntaxHighlighter from 'react-syntax-highlighter'
2import { tomorrowNightBright } from 'react-syntax-highlighter/dist/esm/styles/hljs'
3
4interface Props {
5 value: {
6 code: string
7 language: string
8 }
9}
10
11const CodeBlock = ({ value }: Props) => {
12 const { code, language } = value
13 return (
14 <SyntaxHighlighter
15 showLineNumbers={true}
16 showInlineLineNumbers={true}
17 language={language}
18 style={tomorrowNightBright}
19 customStyle={{
20 padding: '1em',
21 marginBottom: '2em',
22 }}
23 >
24 {code}
25 </SyntaxHighlighter>
26 )
27}
28
29export default CodeBlock
The SyntaxHighlighter also comes with a light installation which doesn’t include all of the themes by default, and you can import just the theme you want for a smaller footprint.
Add the CodeBlock component to PortableText components
In this case, I’ve defined PortableText components inside the PostBody component of the blog, but you can define where you want and import it wherever you use the @portabletext/react component. Add it as one of the block types array members. Here, we can name the value prop for our component to be whatever we want or destructure the keys as needed.
1const ptComponents = {
2 types: {
3 image: ({ value }: any) => {
4 if (!value?.asset?._ref) {
5 return null
6 }
7
8 return (
9 <Image
10 alt={value.alt || ' '}
11 loading="lazy"
12 height={value.height || 500}
13 width={value.width || 500}
14 src={urlForImage(value).url()}
15 style={{
16 width: '100%',
17 marginBottom: '24px',
18 }}
19 />
20 )
21 },
22 code: ({ value }: any) => {
23 return <CodeBlock value={value} />
24 },
25 },
26}
And then pass the components as a prop to the PortableText component along with the entire blockContent field (called ‘body’ here).
1<PortableText
2 value={body}
3 components={ptComponents}
4/>