Replace text in RichText Editor?

It looks like what I want to do should be possible, but I don't think I'm getting the syntax quite right. I would like to automatically find and replace special characters like ® with ® and wrap them in tags.

Looking at the video on serializers here https://youtu.be/US6mirW71ac&t=312, it looks like this should be possible with children.replace, but my IDE is not accepting replace on children since that's JSX Elements directly. I do not want to use labels for this because there are already labels in use and since they cannot currently be nested this won't work.

Any help or ideas would be appreciated. Thanks!

Hi @asummers :slight_smile:

Can you give me a little more information on your code and what it looks like right now, so we're better able to help you out?

What do you mean by that? What are the JSX elements, and what IDE are you using? Are you seeing any error codes or unexpected behaviors related to what you're trying to do?

I'm doing what the video shows as I've linked above, but get this:

Hi @asummers!

This error is likely happening because .replace is a method that only works on strings. Because you're passing heading from PrismicRichText as a prop, children is either not a string, or not read as a string, which is what TypeScript is telling you with children is a type 'Element[ ]'. If you want to use .replace for your htmlSerializer, you'd have to make sure of what children is.

Is this your entire serializer just like in the YouTube video, or is this part of a specific slice? If you can show me the rest of your code, or the way that you're going about this, I can try to help you figure it out with or without replace :slight_smile:

For future me or anyone else who runs into this, I solved this by breaking down the components like this:

<PrismicRichText
          field={slice.primary.header}
          components={{
            heading1: ({ children }) => {
              // Process the children to replace text
              const processedChildren = Array.isArray(children)
                ? children.map((child) => {
                    if (typeof child === 'string') {
                      // Replace the text in the string
                      return child.replace(/oldText/g, 'newText');
                    }
                    return child;
                  })
                : children;

              return (
                <h1 className="leading-9 xl:leading-[75px] text-balance text-center text-3xl xl:text-6xl font-semibold mb-12">
                  {processedChildren}
                </h1>
              );
            },
...
1 Like

Hello @asummers and @Ezekiel

I also need to wrap some placeholders in the text by some html tags, the case is similar as described by OP.

Unfortunately the accepted solution is not working with latest "@prismicio/react": "2.9.1", "@prismicio/next": "1.7.1", "@prismicio/client": "7.12.0".

Error when using PrismicRichText from @prismicio/react:
typeof child === 'string' Invalid 'typeof' check: 'child' cannot have type 'string'

The childern prop available to components?: JSXMapSerializer | JSXFunctionSerializer | undefined is typed as a JSX.Element[] not as a string[]. When debugging, it always looks like an array of JSX Elements with complex structures, not as simple array of strings.

This could be due to the fact that the children prop in the latest versions of Prismic's libraries is always typed as an array of JSX elements (JSX.Element[]) rather than an array of strings or mixed types. This means the typeof child === 'string' check isn't valid because child is never directly a string in this context.

To address this, you can use a recursive approach to traverse the children array and process any text nodes within it. Here's an updated example that should work with the latest versions:

import { PrismicRichText } from "@prismicio/react";

const processChildren = (children) => {
  return children.map((child) => {
    if (typeof child === 'string') {
      // Replace text or wrap placeholders
      return child.replace(/(PLACEHOLDER)/g, '<span class="highlight">$1</span>');
    } else if (React.isValidElement(child) && child.props?.children) {
      // Recursively process nested children
      return React.cloneElement(child, {
        children: processChildren(React.Children.toArray(child.props.children)),
      });
    }
    return child; // Return unmodified child if it's neither a string nor an element with children
  });
};

const MyComponent = ({ slice }) => (
  <PrismicRichText
    field={slice.primary.content}
    components={{
      paragraph: ({ children }) => {
        const processedChildren = processChildren(children);
        return <p>{processedChildren}</p>;
      },
      heading1: ({ children }) => {
        const processedChildren = processChildren(children);
        return <h1>{processedChildren}</h1>;
      },
    }}
  />
);

export default MyComponent;

Let me know how that works for you :slight_smile: