Chris recently put out a neat CodePen Embed Block for the Gutenberg editor in WordPress. It allows you to embed a Pen just by dropping in its URL. From there, you get access to control the size, theme, and the default tabs that render on initial load. Super neat!
class=wp-image-302896Having a live preview of the embedded Pen while writing is so handy!
But it got me thinking: How difficult would it be to recreate it with Sanity Studio’s Portable Text editor? (Spoiler: Not that difficult). Since I already knew how to do it, it took me under seven minutes from start to finish. This tutorial takes you through how to get up and running with a studio, and how to add the schemas and the custom preview component for a CodePen embed.
So this is me recreating @chriscoyier’s CodePen Gutenberg Block for @sanity_io’s rich text editor in less than 7min (3x video). Best thing is, you actually just store the structured data, making it queryable, future proof, and easy to integrate with whatever frontend you prefer. https://t.co/psPn6NtPjz pic.twitter.com/6aSGKerHfO— knut (in SF ??) (@kmelve) January 18, 2020
That felt so cool that I want to teach you how to do it as well. Let’s dive right into it.
Getting Sanity Studio up and running locally
First, you’ll need to install Sanity Studio locally on your machine. In this tutorial we will be using the blog studio that you can initiate from the command line, but you can also check out the different starters on sanity.io/create. You should be able to tag along with one of those too.
This tutorial assumes that you have a bit of knowledge of JavaScript. It will use a bit of React, but only a small part. You should have installed node and npm if you haven’t already.
Oh, and you’ll want the Sanity CLI, which you can snag with the command line:
npm install –global @sanity/cli
Once the installation is done, you can initiate a new Sanity Studio with a new project by running the command sanity init. It will let you log in with your Google or GitHub account (or make a new account with an email/password). Give your project a name and follow the instructions. When given the options for a project template, choose the blog one:
? Select project template
Movie project (schema + sample data)
E-commerce (schema + sample data)
? Blog (schema)
Clean project with no predefined schemas
After completing the steps, change directory (cd) into the new project folder and open it in your favorite code editor. To start the developer server that will also hot reload your studio when you make changes, run sanity start. To stop this server, you press ctrl + C in most command line tools.
Adding the schemas for a CodePen embed
Schemas define which document types that are available in the Studio, and which input fields they have. These schemas are defined in JavaScript objects that you import into the schemas.js file, where they are exported as a function that the Studio translates into its UI. There’s a lot you can do with these schemas, but in this tutorial, we will keep it reasonably simple.
Start with adding a new file inside /yourproject/schemas called codepen.js. Then type in this code:
export default {
name: codepen,
type: object,
title: CodePen Embed,
fields: [
{
name: url,
type: url,
title: CodePen URL
}
]
};
Then you can go to /yourproject/schemas/schema.js and add the following two lines of code to it:
import createSchema from part:@sanity/base/schema-creator;
import schemaTypes from all:part:@sanity/base/schema-type;
import blockContent from ./blockContent;
import category from ./category;
import post from ./post;
import author from ./author;
import codepen from /codepen.js; // <= first import the object export default createSchema({ name: default, types: schemaTypes.concat([ post, author, category, blockContent, codepen // <= add it to the schema types array ]) }); So what did we just do? Well, we have now made this CodePen object available as a type in other schemas in the Studio. In other words, you can now add type: codepen to get those fields anywhere else in the schema code where you add fields. Adding this type to the rich text field is also our next step. Hang on! Adding the CodePen field to the rich text editor Before diving into the code bit, let us take a step back and look at what is going on in terms of the data formats we operate with, and how WordPress and Sanity differ slightly. While Gutenberg stores rich text as JSON in its runtime (which is great!), what developers end up dealing with is mostly this content as HTML and JSON objects inside of HTML comments. Sanity stores and distributes rich text content as Portable Text, which developers then serializes in their frontends. That means that you get fine-grained control over how rich text content is rendered by letting you use custom components for your favorite framework, either its React, Vue, Svelte, or .NET, PHP, or even Markdown. In other words, you store your content as structured data in Sanity’s backend, and then decide how you want to use the data inside your frontend components. But enough exposition, lets get back to the code! Open /schemas/blockContent.js and notice that its of the type array. Yes, rich text is an array of different types, where one of them has to be of the block type (in which text paragraphs are stored). So the simplest way of making rich text is the following schema definition: export default { name: body, type: array, title: Body, of: [ { type: block } ] }; Now, blockContent.js has a bunch of more stuff. You can see styles, lists, marks, and so on. All defining which properties should be available for the author. In the top array, there are two types block and image. We are going to add the third one, codepen: export default { title: Block Content, name: blockContent, type: array, of: [ { type: block // ... }, { type: image, options: { hotspot: true } }, { type: codepen } ] }; Save the file, and thats it! If you now run sanity start in your command line (assuming you havent already), and open the Studio on https://localhost:3333, you should be able to find your new field in the rich text editor under the post type: SanitySanity
If you try out the new button, youll get a modal with the URL field that you defined in the previous section. Feel free to add the URL from a cool CodePen that you have found. We will use this one from the legendary Sara Drasner; its pretty cool.
CodePen Embed Fallback
Just showing the URL value in the editor isnt especially inspiring, though. So lets go ahead and add the actual CodePen embed so we can interact with it directly in the editor!
Adding the CodePen embed as a preview
Open /yourproject/schemas/codepen.js again. Now we are going to make a small React component for our preview. Start by importing React in the top, and the boilerplate for the React component that we will turn into the embed:
import React from react;
const CodePenPreview = ({ value }) => {
return

{JSON.stringify(value, null, 2)}

;
};
export default {
name: codepen,
type: object,
title: CodePen Embed,
fields: [
{
name: url,
type: url,
title: CodePen URL
}
]
};
The JSON.stringify stuff is a temporary little way of outputting the incoming data in a readable manner. You could also use console.log(value), but who has time to open the developer console?
Now you must tell Sanity how to use this component for the preview. As well as which of the fields in the object it should select for the value in the preview component.
import React from react;
const CodePenPreview = ({ value }) => {
return

{JSON.stringify(value, null, 2)}

;
};
export default {
name: codepen,
type: object,
title: CodePen Embed,
preview: {
select: {
url: url
},
component: CodePenPreview
},
fields: [
{
name: url,
type: url,
title: CodePen URL
}
]
};
The editor should look something like this after you saved your changes:
class=wp-image-302246
Cool! Now we want to take the url value and somehow integrate it with a CodePen embed. The easiest way to go about this is to fit the markup for CodePen’s iFrame embed, and fit into our preview component in React.
The original iFrame element will look like this:

If we paste this snippet into our preview component, it will almost work. In order to make it JSX-compatible youll have to some few changes to some of the HTML-attributes. Make sure that you change:
style=width: 100%; to style={{width: 100%}}frameborder=no to frameBorder=noallow-transparency=true to allowTransparencyallow-fullscreen=true to allowFullScreen
You can remove the content (links, etc.) inside of the iframe, because it isnt particularly useful inside the studio. What we should end up with is something like this:
import React from react;
import Codepen from react-codepen-embed;
const CodePenPreview = ({ value }) => {
return (