Data rendered by PrismicRichText not working with GSAP

I'm having a strange issue with PrismicRichText and GSAP animations not working as expected for a Component.

Using the below I am able to render the data to the page, but from an animation perspective GSAP can't find the elements so no animations are applied.

TL.from(title.words, {
duration: 0.8,
y: 100,
ease: 'Power3.easeOut',
stagger: 0.014,
})

<PrismicRichText
field={title}
components={{
heading1: ({ children }) => <h1 className="hero-showreel__content-title">{children}</h1>,
}}
/>

On the other hand, if I render the data using the below, it will animate.

<h1 className="hero-showreel__content-title">{title[0].text}</h1>

The strange thing is I am able to animate text rendered by PrismicRichText with GSAP with no problem from within a Slice using the followng:

TL.from(text.words, {
duration: 0.8,
y: 60,
ease: 'Power3.easeOut',
stagger: 0.0019,
});

<PrismicRichText
field={slice.primary.title}
components={{
heading2: ({ children }) => (
<h2 className="slice--text-image-row__content-title">{children}</h2>
),
}}
/>

Theres nothing really different in the two different approach. Has anyone else expereiced issues with elements not being found when combining PrismicRichText and GSAP?

Hello @luke1,

Thanks for reaching out to us! :slightly_smiling_face:

I am not very familiar with GASP, but from the blog post: Animations with GSAP + React 🌟 - DEV Community, it seems it might require some React-specific code to get it to work. Here’s what I would try:

  • Use useEffect() to create and start the GSAP timeline. This ensures the timeline is only created after <PrismicRichText> has been rendered, which guarantees that the HTML elements are on the page.
  • Use class names to target the elements (e.g. TL.from("hero-showreel__content-title", …))

Let me know if this works out for you.

Thanks,
Racheal.

Hi @racheal.orukele thanks for replying.

Animations are already being implemented across the site in the manner you have mentioned/linked and are working as expected. The issue seems to be with certain ways of implementing data to be displayed when using PrismicRichText .

PrismicRichText is broke only when i change the page. Example when i at Homepage and going to Product page, and back to homepage again. The animation don't work anymore.

Here the video you guys can check:
https://jam.dev/c/15357e4f-6ad5-47fe-8887-1a2bbf7bc558

As you can see in the video, when i back into homepage, Split text of gsap can't select the text on rich text and also image icon

I try to debug and see it only don't work with PrismicRichText, seem like the life cycle conflict each other..Hope you guys can help. Thank you so much

1 Like

Hey @mChanh ! Super cool site design and animations.

Could you share some more of the code so I can help debug? What does the code for that component look like? Are you using Next/react? If so are you using refs? Pages or app router, if Next?

In the meantime the Client package has an asText() helper that would turn this into a simple string and might solve it. Tough to say without knowing the rest of your setup.

1 Like

Hi @alex.trost. Thank for support. I'm using NEXTJS and useGSAP

next 14.2.3/app router

Here is my currently code setup:

  1. I have CharsPop component wrapper to split text as chars by SplitText gsap plugin
  <CharsPop isScrollTrigger={false}>
      <TypoHeading size="140" className={cn(s.home_title, "home-title")}>
           <BasePrismicRichText
                field={slice.primary.title_test}
                components={{
                  paragraph: ({ children }) => <span>{children}</span>,
                  image: ({ node }) => (
                    <span className={cn(s.home_icon, "home-icon")}>
                      <PrismicImage
                        field={node}
                        alt="ic"
                        className="home-icon_ic"
                      />
                    </span>
                  ),
                }}
              />
            </TypoHeading>
   </CharsPop>
  1. At HeroComponent, useGSAP to animation each char w timeline
  useGSAP(
    () => {
      const q = gsap.utils.selector(container);

      //selector all char element in heroContainer
      const chars = q(".char");

      //create timeline to animation each char

      const tl = gsap.timeline();

      tl.to(chars, {
        scale: 1,
      });
    },
    { scope: container }
  );
  1. It is my CharsPop component wrapper something like this:
"use client";
import React, { useRef } from "react";
import { SplitText, useGSAP, DURATION, gsap } from "@libs/gsap";

const CharsPop = ({ children, isScrollTrigger = true }) => {
  const refContent = useRef(null);

  useGSAP(
    () => {
      const splitChars = new SplitText(refContent.current, {
        type: "chars,words",
        charsClass: "char",
      });

      gsap.set(splitChars.chars, { scale: 0 });

      isScrollTrigger &&
        gsap.to(splitChars.chars, {
          scale: 1,
          stagger: DURATION / 24,
          scrollTrigger: {
            trigger: refContent.current,
            start: `top+=${86}% bottom`,
          },
        });
    },
    { scope: refContent, dependencies: [] }
  );
  if (!React.isValidElement(children)) {
    return <div>Error: Invalid children element</div>;
  }

  return React.cloneElement(children, { ...{ ref: refContent } });
};
export default React.memo(CharsPop);

If i using prismic.asText it will missing the image inline with the title text. So that why i need to use prismicRichText :disappointed_relieved: You have any advice to give me going right direction!?, Thank for help me

Many thanks :saluting_face: :saluting_face:

Thanks for sharing all that @mChanh! I love your implementation for CharsPop. I’ve done this kind of split text implementation before and I really love how you did it.

Could you toss a console.log right after this line to see what chars is at that moment?
const chars = q(".char");

I have a hunch either useGSAP isn’t rerunning or we aren’t getting anything in that chars array the second time around.

1 Like