How to render and copy code blocks using libraries such as @sanity/code-input and react-refractor
In this blog post, I will share my journey of implementing code block rendering in a Next.js and Sanity project. Initially, I used to include images instead of actual code blocks in my blog posts. However, in order to make my articles more accessible and developer-friendly, I decided to tackle this challenge. After overcoming various hurdles, I successfully achieved the desired outcome. Now, I want to share the process of how I accomplished it.
Before diving into the implementation details, make sure you have the following dependencies installed:
@sanity/code-input package (version 4.1.1 or higher)
react-refractor package
@portabletext/react package
uuid package
To begin, install the @sanity/code-input package by running the following command:
yarn add @sanity/code-input@^4.1.1
Next, open the sanity.config.ts file and add the codeInput plugin to the plugins array:
import { codeInput } from '@sanity/code-input'
export default defineConfig({
// ...
plugins: [codeInput()]
})
In your post.ts schema file, locate the existing defineField block responsible for rendering the body content. Add the following code block definition within the defineArrayMember section:
defineArrayMember({
type: 'code',
name: 'myCodeField',
title: 'Code with all options',
options: {
language: 'javascript',
languageAlternatives: [
{ title: 'Javascript', value: 'javascript' },
{ title: 'TypeScript', value: 'typescript' },
{ title: 'tsx', value: 'tsx' },
],
withFilename: true,
},
}),
To render the post body, we will be using the PortableText component from the @portabletext/react package. Modify your code as follows:
import { PortableText } from '@portabletext/react'
;<PortableText onMissingComponent={false} value={post.body} components={RichTextComponents} />
In order to customize the rendering of code blocks, we need to define a custom component within the RichTextComponents object. Install the react-refractor package by running:
yarn add react-refractor
Update your code as shown below:
import Refractor from 'react-refractor'
import { v4 as uuid } from 'uuid'
export const RichTextComponents = {
types: {
// ...
myCodeField: ({ value }) => {
const iconId = uuid()
return (
<div className="text-dark-textDescription my-8 bg-gray-700 px-4 py-3">
<Refractor language="js" value={value.code} />
<div className="flex justify-end">
<ClipboardIcon
id={`clipboard-icon-${iconId}`}
className="h-6 w-6 cursor-pointer delay-75 hover:text-gray-500"
/>
</div>
<script
dangerouslySetInnerHTML={{
__html: `
document.getElementById('clipboard-icon-${iconId}').addEventListener('click', function() {
const textToCopy = ${JSON.stringify(value.code)};
const textarea = document.createElement('textarea');
textarea.value = textToCopy;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
});
`
}}
/>
</div>
)
}
}
}
Update your code as shown below:
By following the steps outlined in this blog post, I successfully integrated code block rendering into my Next.js and Sanity project. I overcame the initial challenge of including images in place of code blocks, and now my blog posts are more accessible and developer-friendly. I hope this step-by-step guide helps you in implementing code block rendering in your own projects.