Back to Changelog
v0.3.0

December 12, 2023

Instantiate Components inside Rich Text Blocks

Often times, we’ll find ourselves wanting to render a Tweet in the body of our blog posts, or a callout, or any other thing specific to our website’s content needs that isn’t strictly formatting. Well, with this addition, now we can! Let’s see how.


Enabling custom components

To get started, we need to configure our rich text block to support a component. In this example, we'll create a Tweet component and configure it to be instantiated within the body of our blog posts.

Once we've created our Tweet component, we’ll select our rich text block and check “Tweet” in the “Allowed component types“ constraint. This will expose it in our rich text’s slash command.

Now, we can do /tweet to add a Tweet right in the body of our post. Additionally, we can use a custom component to “mark” a piece of text (like we do so when we make it bold or italic), available via the bubble menu. This is useful for adding custom popovers or tooltips to specific pieces of text.

Render them with the <RichText /> component

To render our custom components, we'll use <RichText />. We’ll install basehub via NPM, and import { RichText } from 'basehub/react'. This component is designed to facilitate the rendering of rich text data. It's straightforward to use and works well with TypeScript.

This is a small example of how we’ll use it alongside the amazing react-tweet to render static tweets:

import { notFound } from "next/navigate"
import { basehub } from "basehub";
import { RichText } from "basehub/react";
import { Tweet } from "react-tweet";

export default async function BlogPost({ slug }) {
  const { blog } = await basehub({ next: { revalidate: 60 } }).query({
    blog: {
      posts: {
        items: {
          __args: { first: 1, __sys_slug: { eq: slug } },
          _title: true,
          body: {
            json: {
              content: true,
              blocks: {
                on_TweetComponent: {
                  __typename: true,
                  _id: true,
                  url: true,
                },
              },
            },
          },
        },
      },
    },
  });
  const [entry] = blog.posts.items;
  if (!entry) return notFound();

  return (
    <main>
      <h1>{entry._title}</h1>
      <RichText
        blocks={entry.body.json.blocks}
        components={{
          TweetComponent: ({ url }) => {
            return <Tweet id={new URL(u).pathname.split("/").pop()} />;
          },
        }}
      >
        {entry.body.json.content}
      </RichText>
    </main>
  );
}

The body of the component fetches the data with basehub, and the return uses <RichText /> to render the data. Via components, we pass our custom renderers. It’s important to send through the blocks prop so that <RichText /> can match the references to the custom blocks.

This is just a quick overview of what you can do with custom blocks inside rich text. Expect more examples soon, as we ramp up our marketing and documentation efforts.

Oh, and by the way, we hope you enjoy our new Changelog!