Blog thumbnail with a modern and engaging look, where the main visual is code blocks containing TypeScript code snippets

Code Snippets with @sanity/code-input and Prism React Renderer

  • Sanity
  • Next.js

As developers and content creators, we all know the importance of clearly presenting code snippets. They’re not just chunks of text but a gateway to better understanding and engagement. While the foundational work by dwe.fi and Bjørge Næss has paved the way, it’s time to embrace the updates that make code representation in Sanity even better.

I will be showing off how to get it setup in your Next.js and Sanity project, but the information below can still be used a reference to implement it else where.

What’s New in @sanity/code-input

Since its introduction, @sanity/code-input has undergone several updates. It includes switching over to CodeMirror as the code editor from the previous AceEditor and a different way to highlight lines.

Installation and Setup

Installing @sanity/code-input

Start by adding the plugin to your project:

1yarn add @sanity/code-input

Add to your config file

sanity.config.ts

1import { defineConfig } from 'sanity';
2import { codeInput } from '@sanity/code-input';
3
4export default defineConfig({
5 // Other config settings
6
7 plugins: [
8 // Other plugins
9 codeInput(),
10 ],
11});

Integrating Code Input into Block Content

In your schema file, add the code type to your block content array of types:

block.ts

1defineField({
2 name: 'copy',
3 type: 'array',
4 of: [
5 defineArrayMember({
6 type: 'block',
7 }),
8 defineField({
9 name: 'code',
10 type: 'code',
11 options: {
12 withFilename: true, // Optional
13 },
14 }),
15 ],
16}),

This snippet enables code input blocks throughout your content, complete with syntax highlighting.

Data model and type

1{
2 _key: '123' // Shows up as added to block content
3 _type: 'code',
4 language: 'js',
5 highlightedLines: [1, 2],
6 code: 'const foo = "bar"\nconsole.log(foo.toUpperCase())\n// BAR',
7 filename: 'filename.ts' // Shows up if enabled
8}

1export type TCodeBlock = {
2 language: string,
3 highlightedLines: number[],
4 code: string;
5 filename: string;
6};

Rendering Block Content with @portabletext/react

When you query your content from Sanity, you need a way to render the block content in your front-end application. The @portabletext/react package is a powerful tool for this, allowing you to transform your queried block content into React components seamlessly.

Installing @portabletext/react

1yarn add @portabletext/react @portabletext/types

Setting Up the PortableText Custom Component

Once installed, you can import and use the PortableText component to render your content. Here’s a simple setup:

custom-portable-text.tsx

1import { PortableText, PortableTextComponents } from '@portabletext/react';
2import { PortableTextBlock } from '@portabletext/types';
3
4import { TCodeBlock } from '@/types';
5
6const components: PortableTextComponents = {
7 block: {
8 h1: ({ children }) => {
9 return (
10 <h1 className="scroll-m-32 text-2xl">
11 {children}
12 </h1>
13 );
14 },
15 },
16 types: {
17 myCodeField: ({ value }: { value: TCodeBlock }) => {
18 return <CodeBlock code={value} />;
19 },
20 },
21};
22
23export default function CustomPortableText({
24 value,
25}: {
26 value: PortableTextBlock[];
27}) {
28 return <PortableText components={components} value={value} />;
29}

In the above example, components is an object that tells @portabletext/react how to render different types of content. For code blocks, we're pointing it to use our custom CodeBlock component that we will set up now to render with prism-react-renderer.

Styling with Prism React Renderer

To further enhance the appearance of your code blocks, install prism-react-renderer:

1yarn add prism-react-renderer

Basic Setup

The setup below custom component or highlighter component which is used to add support for code block highlights and also includes language support.

code-block.tsx

1import { Highlight } from 'prism-react-renderer';
2
3import { TCodeBlock } from '@/types';
4
5type Props = {
6 code: TCodeBlock;
7};
8
9export default function CodeBlock({ code: { code, filename, language } }: Props) {
10 return (
11 <Highlight code={code} language={language}>
12 {({ style, tokens, getLineProps, getTokenProps }) => (
13 <pre style={style}>
14 <p className="opacity-70">{filename}</p>
15 {tokens.map((line, i) => (
16 <div key={i} {...getLineProps({ line })}>
17 <span>{i + 1}</span>
18 {line.map((token, key) => (
19 <span key={key} {...getTokenProps({ token })} />
20 ))}
21 </div>
22 ))}
23 </pre>
24 )}
25 </Highlight>
26 );
27}
28

The setup above will get your code block looking like the following:

Screenshot of a blog post showing off the code blocks in play
Screenshot of a blog post showing off the code blocks in play

Customise it Further

I went ahead and imported the themes from prism-react-renderer to style the code block like one of the theme extensions you can find in VS Code.

I also gave the span which displays the line numbers the Tailwind classes inline-block w-8 select-none. I added the display and width property to make the code line up on each line as once you go from a single digit to double digits it slightly moves the code to the right.

I added the user select property so if someone wishes to copy the code, they can do so without copying the line numbers too.

code-block.tsx

1import { Highlight, theme } from 'prism-react-renderer';
2
3export default function CodeBlock({ code: { code, filename, language } }: Props) {
4 return (
5 <Highlight theme={theme.shadesOfPurple} code={code} language={language}>
6 {({ style, tokens, getLineProps, getTokenProps }) => (
7 <pre style={style}>
8 <p className="opacity-70">{filename}</p>
9 {tokens.map((line, i) => (
10 <div key={i} {...getLineProps({ line })}>
11 <span className="inline-block w-8 select-none">{i + 1}</span>
12 {line.map((token, key) => (
13 <span key={key} {...getTokenProps({ token })} />
14 ))}
15 </div>
16 ))}
17 </pre>
18 )}
19 </Highlight>
20 );
21}
22

Just as I posted this I remembered about showing off the lines you highlight. To highlight lines in Sanity open up the code block and click on the line you wish to highlight, the line should get a yellow background if its highlighted.

Now below for how to setup it so it renders highlighted lines on the frontend too.

code-block.tsx

1import { Highlight, theme } from 'prism-react-renderer';
2
3export default function CodeBlock({ code: { code, filename, language, highlightedLines } }: Props) {
4 return (
5 <Highlight theme={theme.shadesOfPurple} code={code} language={language}>
6 {({ style, tokens, getLineProps, getTokenProps }) => (
7 <pre style={style}>
8 <p className="opacity-70">{filename}</p>
9 {tokens.map((line, i) => (
10 <div key={i} {...getLineProps({ line })} className={highlightedLines.includes(i + 1) ? 'bg-background/50' : undefined}>
11 <span className="inline-block w-8 select-none">{i + 1}</span>
12 {line.map((token, key) => (
13 <span key={key} {...getTokenProps({ token })} />
14 ))}
15 </div>
16 ))}
17 </pre>
18 )}
19 </Highlight>
20 );
21}
22

Conclusion

Upgrading your code blocks with these tools not only improves readability but also the overall aesthetic of your content. It's an investment in your audience's experience on your site. Dive into these updates and see how your code snippets can shine!

Looking to enhance your digital content with beautifully styled code blocks? If integrating these features sounds daunting, worry not—I’m here to help. Get in touch if you need assistance implementing these updates, or if you're interested in more advanced customizations. Let’s make your code stand out!