Strange RichText rendering error, stuck trying to debug a clear explanation
See original GitHub issueHello! I originally posted this on the forum but I was asked to move it here. I’m copying all of the detail from that post to this issue.
Hello! We have been dealing with quite a weird issue when attempting to render a headline using the Prismic RichText React component.
Our product manager (@justduett) noticed that, when an iOS device loaded our website, there was a weird “jump” a few pixels down the page. You can see a video of this here.
- This only occurs on real iOS devices, you cannot replicate this in Safari on desktop.
- This only seems to occur on “first page load” (or with cache disabled).
Our immediate fix was to simply .scrollTo(0, 0), but I wanted to understand what was actually causing this.
I went through a number of debugging steps to find the culprit, but ultimately I resorted to just deleting entire chunks of the website until the loading bug stopped. Through this brute force method, I was able to find the single header on the site responsible for this bug.
This header (which uses a style and font in numerous other places higher up the page) is rendered by the Prismic RichText component. Now, my first instinct was to investigate the CSS, animations, and other surrounding markup. However, no matter what I tried getting rid of, the bug persisted.
So then I tried something silly. I wrote pure HTML to match what the RichText was outputting. And the bug went away.
<h2>Features developers love.<br/>Experiences <br/>customers trust.</h2>
<RichText render={data.primary.section_title}></RichText>
And here is the HTML comparison, they’re identical.
For reference, here is the data supplied to the RichText prop,
[
{
"type": "heading2",
"text": "Features developers love.\nExperiences \ncustomers trust.",
"spans": []
}
]
This seemed really strange to me. Why is the same HTML markup working if I just don’t render it with Prismic’s RichText component? Surely Prismic isn’t calling .scrollTo
anywhere, and we use this same code pattern in numerous places on this page, why would only this title be having this issue?
So I tried a number of things, like removing the line breaks that got passed into the RichText component, and it made no difference. The only change I could make to “fix” the RichText component was to either make it 1 word, or reduce the font size to something very small. Obviously neither of these are “fixes” and I don’t quite understand why they worked, but I think it might help provide useful context as folks try to wrap their head around this.
So, we were on an old version of Prismic’s React library, 1.3.4, and I thought it was worth investigating if there may in fact be this unexplainable bug in Prismic’s library. So I pulled the library down, npm link
’d it to our application, and started debugging.
I wrapped my debug code with the following conditional, so it only ran for the specific title we’re investigating here,
if (render && JSON.stringify(render).includes('Features')) {
Within that conditional, I returned the following output,
const output = React.createElement('h2', null, [
'Features developers love.',
React.createElement('br'),
'Experiences',
React.createElement('br'),
'customers trust.',
]);
Yet again, same expected markup as before, and the bug went away. Wat
So now I started comparing the output of what Prismic was generating with the React API, to what I would expect it to generate with something like what I wrote above.
What I found is that, Prismic was nesting elements in an array within another array,
"props": {
"children": [
[
"Features developers love.",
This seemed wrong, and while I didn’t understand entirely how it contributed to the literal scroll issue I started investigating (maybe this created some weird rendering “blip” with React?), I figured this might be a sign we should bite the bullet and upgrade Prismic.
Well, one huge Prismic upgrade later, the issue is still there. And yet again, if I npm link
the latest version of @prismicio/react
to our website, and inject the following code into the useMemo
hook of PrismicRichText
,
if (props.debug) {
return (
<h2>
Features developers love.
<br />
Experiences <br />
customers trust.
</h2>
The iOS scroll problem entirely goes away. The problem only occurs if I return the serialized
variable (which seems to stem from a mix of this repo & the react package). The serialized
variable literally matches the same HTML I wrote above.
I have hit a wall trying to figure this one out. I realize this is extremely hard to debug, and I would be happy to provide any additional information that could be helpful.
I also want to note, if you attempt going to our website to see for yourself,
- The scrollTo fix is still in place
- We have not shipped the Prismic upgrade yet
- The html markup of the header I screen-shotted above looks like
<div>
elements if you inspect element on our live site. This is due to how a contractor implemented animations, but I assure you this entire time I have been debugging with that code entirely removed. - I do not have a fully re-producable code snippet I can share at this time, however, if after reading this you feel confident you can investigate this further, I can attempt to recreate a reproducible example
Versions
- @prismicio/react: 2.0.3
- react: 17.0.2
- node: 16.13.1
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:9 (5 by maintainers)
Top GitHub Comments
Hey @itsjoekent, thank you so much for the very detailed triaging and description. This is definitely a strange bug. Thankfully I have an iPhone to test this. 😄
I’ll be looking into this today and will post here once I understand the problem better.
@itsjoekent This was fun to debug! I think I figured out the issue and how to fix it. If I’m correct, it’s not caused by
<PrismicRichText>
. 🙂Why does the issue happen?
The issue happens because the
tt-bluescreens
font is not available initially. This is why the issue only occurs on an empty cache. Something could be calculating something about the canvas and changing the scroll position before the font is available (are there anyscrollTo
s?).Before the font is loaded, headings use the fallback
sans-serif
font. This font is much wider thantt-bluescreens
. Long, non-wrapping words cause the text to overflow outside the Slice’s bounding box. The viewport is configured to shrink the page’s canvas to fit within the viewport’s width. This ultimately messes with the page’s canvas size during scroll position calculation (if that’s what the site is doing).As you know, the large layout shift was caused by the
one_picture_section
Slice. This is a result of its heading content, not because of its use of<PrismicRichText>
. If you were to replace the heading content with short words, like “Hello World,” the issue would likely not occur.If you want to see what I’m talking about re: overflow, remove
tt-bluescreens
from thefonts.heading
theme value. On an iOS device, the heading will extend far outside the Slice’s container.Why did replacing PrismicRichText with HTML seem to fix the issue?
The replacement HTML you tested was actually not identical to what was rendered from Prismic.
There should be an
between “developers” and “love”. You can confirm this by inspecting the HTML when rendering with<PrismicRichText>
.With the correct content in place, the issue occurs exactly as with
<PrismicRichText>
.This difference is significant because
is a “non-breaking” space. This means the browser needs to render the string “developers love” with no line breaks. Combined with wide width ofsans-serif
(beforett-bluescreens
is loaded), this causes the Slice component to overflow significantly.Possible fixes
overflow-x: hidden
around the Slice Zone.tt-bluescreens
is loaded.
in the heading. This only appears to fix the issue, but in reality, any long word and sufficiently small display (even my iPhone XS still sees a slight vertical shift) will still trigger this issue. Still, this is a quick, easy, sort-of fix for this specific issue.I’m pretty sure this is what’s happening. If it still seems to be directly related to
<PrismicRichText>
, please let me know and I will take another look.