Describe the bug
The article page (e.g. pl/pomoc-i-wsparcie/gwarancja-i-naprawa/zarzadzanie-narzedziami-powerpro-online) triggers Next.js hydration errors caused by invalid HTML nesting: <p> elements containing block-level descendants (e.g. <div> from ImgComp or nested <p> from HTML).
Specific errors:
- Error 1:
In HTML, p cannot be a descendant of <div>
- Error 2:
<p> cannot contain a nested <div>
- Error 3:
Hydration failed because... Invalid HTML tag nesting
To Reproduce
- Run
npm run dev
- Open the article page
/pl/pomoc-i-wsparcie/gwarancja-i-naprawa/zarzadzanie-narzedziami-powerpro-online
- Check browser console for hydration errors
Expected behavior
Article content (text, images, blockquotes, lists) should render without hydration errors. No invalid HTML nesting.
Root cause
- RichText (
packages/ui/src/components/RichText/RichText.tsx) uses markdown-to-jsx with a p override that renders paragraphs as <p> via TypographyComp
- Markdown/HTML parsing wraps images in
<p> tags (CommonMark behavior). When the img override (ImgComp) runs, it wraps each image in a <div> for layout
- Result:
<p><div><Image/></div></p> — invalid HTML because <p> cannot contain block-level elements like <div>
- Zendesk content: When articles come from Zendesk, raw HTML can produce nested
<p><p>...</p></p>, which also violates HTML spec
Proposed fix
Change the p override in RichText to use tag: 'div' instead of default p. This allows block descendants and fixes both the image-in-paragraph and nested-paragraph cases while preserving visual styling.
Desktop:
- OS: Windows/macOS
- Browser: Chrome/Safari/Firefox (any)
- Version: N/A (reproducible across browsers)
Additional context
- Primary fix file:
packages/ui/src/components/RichText/RichText.tsx
- Optional hardening: Add HTML sanitization in
packages/integrations/zendesk/src/modules/articles/zendesk-article.mapper.ts if Zendesk produces problematic markup
- Affected blocks: Article, TicketDetails, ServiceDetails, FAQ, NotificationDetails, TicketSummary — any block using RichText
This repo is using Opire - what does it mean? 👇
💵 Everyone can add rewards for this issue commenting /reward 100 (replace 100 with the amount).
🕵️♂️ If someone starts working on this issue to earn the rewards, they can comment /try to let everyone know!
🙌 And when they open the PR, they can comment /claim #779 either in the PR description or in a PR's comment.
🪙 Also, everyone can tip any user commenting /tip 20 @marcinkrasowski (replace 20 with the amount, and @marcinkrasowski with the user to tip).
📖 If you want to learn more, check out our documentation.
Describe the bug
The article page (e.g.
pl/pomoc-i-wsparcie/gwarancja-i-naprawa/zarzadzanie-narzedziami-powerpro-online) triggers Next.js hydration errors caused by invalid HTML nesting:<p>elements containing block-level descendants (e.g.<div>from ImgComp or nested<p>from HTML).Specific errors:
In HTML, p cannot be a descendant of <div><p> cannot contain a nested <div>Hydration failed because... Invalid HTML tag nestingTo Reproduce
npm run dev/pl/pomoc-i-wsparcie/gwarancja-i-naprawa/zarzadzanie-narzedziami-powerpro-onlineExpected behavior
Article content (text, images, blockquotes, lists) should render without hydration errors. No invalid HTML nesting.
Root cause
packages/ui/src/components/RichText/RichText.tsx) uses markdown-to-jsx with apoverride that renders paragraphs as<p>via TypographyComp<p>tags (CommonMark behavior). When theimgoverride (ImgComp) runs, it wraps each image in a<div>for layout<p><div><Image/></div></p>— invalid HTML because<p>cannot contain block-level elements like<div><p><p>...</p></p>, which also violates HTML specProposed fix
Change the
poverride in RichText to usetag: 'div'instead of defaultp. This allows block descendants and fixes both the image-in-paragraph and nested-paragraph cases while preserving visual styling.Desktop:
Additional context
packages/ui/src/components/RichText/RichText.tsxpackages/integrations/zendesk/src/modules/articles/zendesk-article.mapper.tsif Zendesk produces problematic markupThis repo is using Opire - what does it mean? 👇
💵 Everyone can add rewards for this issue commenting
/reward 100(replace100with the amount).🕵️♂️ If someone starts working on this issue to earn the rewards, they can comment
/tryto let everyone know!🙌 And when they open the PR, they can comment
/claim #779either in the PR description or in a PR's comment.🪙 Also, everyone can tip any user commenting
/tip 20 @marcinkrasowski(replace20with the amount, and@marcinkrasowskiwith the user to tip).📖 If you want to learn more, check out our documentation.