Skip to content

Conversation

@Lakshya-2440
Copy link

@Lakshya-2440 Lakshya-2440 commented Jan 3, 2026

Description

Problem

The website intermittently crashed with React production errors:
Error #425
Error #418
Error #423

This occurred more frequently when:
Switching responsive viewports in DevTools (e.g. iPhone XR, iPhone 14 Pro Max, iPhone SE)
Using a non-UTC timezone (e.g. IST – UTC+5:30)
Using non-US locale or date formatting
Running on macOS

These conditions caused differences between server-rendered HTML and client-rendered output, resulting in hydration failures.

Root Cause
Viewport-dependent logic ran during SSR, producing markup that differed from the client render.
Locale and timezone-dependent formatting (dates, numbers) produced inconsistent output between server and client.
React hydration failed when DevTools device emulation or user locale differed from the server environment.

Solution

This PR ensures deterministic server rendering by:
Deferring viewport-specific logic to the client side only
Making locale- and timezone-dependent rendering consistent between SSR and CSR
Preventing hydration mismatches by aligning server and client output

As a result, hydration now succeeds reliably across:
All screen sizes
Device emulation modes
Timezones
Locales
Operating systems (including macOS)

Tested On

macOS (Chrome, Safari)
Multiple responsive device presets
Non-UTC timezones (IST)
Non-US locale/date formats

image

Related issue(s)
Fixes #4858

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced rendering consistency for date and time information in calendar and announcement components.
    • Optimized date computation logic sequencing in event post displays to ensure proper data flow during rendering.

✏️ Tip: You can customize this high-level summary in your review settings.

@asyncapi-bot
Copy link
Contributor

asyncapi-bot commented Jan 3, 2026

We require all PRs to follow Conventional Commits specification.
More details 👇🏼

 No release type found in pull request title "Fixed intermittent React hydration crash on macOS when changing respo…". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

📝 Walkthrough

Walkthrough

These changes suppress React hydration warnings across three date/time-rendering components by adding suppressHydrationWarning attributes to elements affected by timezone and locale-dependent formatting differences between server and client renders, addressing intermittent hydration crashes on viewport changes.

Changes

Cohort / File(s) Summary
Hydration warning suppression
components/Calendar.tsx, components/campaigns/AnnouncementRemainingDays.tsx, components/navigation/EventPostItem.tsx
Added suppressHydrationWarning to span and p elements that render date/time information affected by server-side vs. client-side locale/timezone formatting inconsistencies. No logic changes; attribute-only additions.
Date computation reordering
components/navigation/EventPostItem.tsx
Moved localDate logic declarations (localTime and currentDate) to after postDate initialization to ensure proper variable ordering and availability for date comparisons. Preserves existing behavior and validity checks.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 Time zones and locales once caused hydration crashes,
But now with a whispered suppressHydrationWarning flash,
Date-rendered spans and paragraphs align,
Server and client in perfect time!
No more viewport flickers, no more timezone thrash—
The AsyncAPI site is restored to stash. ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main fix (React hydration crash on macOS when changing viewport), which directly addresses the primary change in the PR.
Linked Issues check ✅ Passed The PR implements the solution to fix hydration mismatches by adding suppressHydrationWarning attributes and ensuring server/client rendering alignment across components, addressing all coding requirements in issue #4858.
Out of Scope Changes check ✅ Passed All changes (suppressHydrationWarning additions and date computation reordering) are directly scoped to fixing hydration mismatches, with no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@asyncapi-bot asyncapi-bot requested a review from CBID2 January 3, 2026 09:56
@netlify
Copy link

netlify bot commented Jan 3, 2026

Deploy Preview for asyncapi-website ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 48f7c52
🔍 Latest deploy log https://app.netlify.com/projects/asyncapi-website/deploys/6958e815b8751f0008013b30
😎 Deploy Preview https://deploy-preview-4862--asyncapi-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@asyncapi-bot
Copy link
Contributor

asyncapi-bot commented Jan 3, 2026

⚡️ Lighthouse report for the changes in this PR:

Category Score
🔴 Performance 37
🟢 Accessibility 98
🟢 Best practices 92
🟢 SEO 100
🔴 PWA 33

Lighthouse ran on https://deploy-preview-4862--asyncapi-website.netlify.app/

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
components/campaigns/AnnouncementRemainingDays.tsx (1)

14-35: Consider client-only rendering for truly dynamic countdown.

Unlike static event dates, this component calculates relative time (days/hours/minutes until the event), which genuinely differs between server build time and client page load time. While suppressHydrationWarning is more justifiable here than for static dates, the server-rendered countdown will be stale by the time users see it.

A better approach is to defer rendering to the client so the countdown reflects the actual current time:

🔎 Recommended: Client-only rendering
 export default function AnnouncementRemainingDays({ dateTime, eventName }: AnnouncementRemainingDaysProps) {
+  const [text, setText] = React.useState('');
+
+  React.useEffect(() => {
     const date = moment(dateTime);
     const now = moment();
     const days = date.diff(now, 'days');
     const hours = date.diff(now, 'hours');
     const minutes = date.diff(now, 'minutes');
 
-    let text = '';
+    let countdownText = '';
 
     if (days >= 1) {
-      text = `${days} ${days === 1 ? 'day' : 'days'}`;
+      countdownText = `${days} ${days === 1 ? 'day' : 'days'}`;
     } else if (hours > 1) {
-      text = 'A few hours';
+      countdownText = 'A few hours';
     } else if (minutes > 1) {
-      text = 'A few minutes';
+      countdownText = 'A few minutes';
     }
+    
+    setText(countdownText);
+  }, [dateTime]);
 
   return (
-    <span className='countdown-text-gradient block font-extrabold' data-testid='AnnouncementRemainingDays-text' suppressHydrationWarning>
+    <span className='countdown-text-gradient block font-extrabold' data-testid='AnnouncementRemainingDays-text'>
       {text} until {eventName}
     </span>
   );

This ensures the countdown displays the correct time when the page loads in the browser, rather than a stale server-rendered value.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cd3cd7 and 48f7c52.

📒 Files selected for processing (3)
  • components/Calendar.tsx
  • components/campaigns/AnnouncementRemainingDays.tsx
  • components/navigation/EventPostItem.tsx
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-12-29T14:21:28.216Z
Learnt from: sammy200-ui
Repo: asyncapi/website PR: 4804
File: components/navigation/Filter.tsx:32-41
Timestamp: 2025-12-29T14:21:28.216Z
Learning: In the asyncapi/website repository, when you intentionally omit dependencies in React hooks (e.g., useEffect, useMemo), add an eslint-disable comment with an explanatory note above the line to justify the design choice. For example: // eslint-disable-next-line react-hooks/exhaustive-deps: intentionally omitting dependencies to avoid unnecessary re-runs. This helps reviewers understand the rationale and keeps lint guidance informative.

Applied to files:

  • components/campaigns/AnnouncementRemainingDays.tsx
  • components/Calendar.tsx
  • components/navigation/EventPostItem.tsx
📚 Learning: 2024-10-11T07:38:35.745Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.

Applied to files:

  • components/Calendar.tsx
📚 Learning: 2024-10-11T11:32:30.226Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/navigation/BlogPostItem.tsx:95-119
Timestamp: 2024-10-11T11:32:30.226Z
Learning: In the `BlogPostItem` component (`components/navigation/BlogPostItem.tsx`), nesting `<a>` tags inside the parent `Link` component leads to hydration issues; therefore, we should avoid nesting `<a>` tags inside `Link` components in this component.

Applied to files:

  • components/Calendar.tsx
  • components/navigation/EventPostItem.tsx
📚 Learning: 2024-10-11T10:46:58.882Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.

Applied to files:

  • components/Calendar.tsx
📚 Learning: 2024-10-11T11:17:32.246Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/navigation/BlogPostItem.tsx:80-87
Timestamp: 2024-10-11T11:17:32.246Z
Learning: In the `BlogPostItem` component, the parent `Link` wraps the entire content, so inner `Link` components around the title and excerpt are unnecessary.

Applied to files:

  • components/navigation/EventPostItem.tsx
📚 Learning: 2025-12-23T06:30:43.275Z
Learnt from: katara-Jayprakash
Repo: asyncapi/website PR: 4760
File: components/layout/GenericPostLayout.tsx:50-52
Timestamp: 2025-12-23T06:30:43.275Z
Learning: In the asyncapi/website repository, `GenericPostLayout` (components/layout/GenericPostLayout.tsx) is used for rendering about pages, not blog posts, even though it accepts a post typed as `IPosts['blog'][number]`. The type is reused for convenience. Blog posts use `BlogLayout.tsx` instead. When reviewing EditPageButton usage in GenericPostLayout, `contentType='about'` is the correct value.

Applied to files:

  • components/navigation/EventPostItem.tsx
📚 Learning: 2025-06-03T06:48:09.304Z
Learnt from: iambami
Repo: asyncapi/website PR: 4111
File: markdown/blog/community-spotlight-aayush.md:3-3
Timestamp: 2025-06-03T06:48:09.304Z
Learning: For AsyncAPI community spotlight blog posts, the frontmatter date should reflect the actual publication date, not the month being highlighted in the spotlight. For example, a "May Community Spotlight" can be published in June with a June date in the frontmatter.

Applied to files:

  • components/navigation/EventPostItem.tsx
🪛 GitHub Actions: PR testing - if Node project
components/campaigns/AnnouncementRemainingDays.tsx

[error] 32-32: Prettier formatting issue: Replace spacing around elements as suggested.

components/Calendar.tsx

[error] 49-49: Prettier formatting issue: Replace {moment(event.date).format('D')} with a multi-line version as suggested.

components/navigation/EventPostItem.tsx

[error] 53-54: Expected blank line after variable declarations and before following statement.

⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Redirect rules - asyncapi-website
  • GitHub Check: Header rules - asyncapi-website
  • GitHub Check: Pages changed - asyncapi-website
🔇 Additional comments (1)
components/navigation/EventPostItem.tsx (1)

49-58: Variable reordering looks correct.

Moving postDate initialization before the active check ensures the date is available for validity checking and comparison. The logic correctly handles invalid dates and past events.

<a href={event.url} className='mb-1 mt-2 flex grow flex-col items-start sm:flex-row sm:items-center'>
<div className='inline-flex h-12 min-w-12 flex-row rounded-full bg-pink-500 font-bold text-white'>
<span className='flex-1 self-center text-center'>{moment(event.date).format('D')}</span>
<span className='flex-1 self-center text-center' suppressHydrationWarning>{moment(event.date).format('D')}</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix Prettier formatting issue.

The pipeline reports a formatting error on this line. Run npm run format or yarn format to apply the required formatting.

🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[error] 49-49: Prettier formatting issue: Replace {moment(event.date).format('D')} with a multi-line version as suggested.

🤖 Prompt for AI Agents
In components/Calendar.tsx around line 49, the line with the span element fails
Prettier formatting; run the project's formatter (npm run format or yarn format)
or apply Prettier rules to reformat this line (adjust spacing/quotes/className
placement as Prettier requires) and then stage the updated file so the pipeline
passes.

Comment on lines +49 to 56
<span className='flex-1 self-center text-center' suppressHydrationWarning>{moment(event.date).format('D')}</span>
</div>
<div className='grow text-left sm:mt-0 sm:pl-6'>
<h2 className='title-font font-medium text-gray-900 hover:text-gray-500'>{event.title}</h2>
<p className='text-gray-600'>
<p className='text-gray-600' suppressHydrationWarning>
{moment(event.date).local().format('LLLL')} UTC
{moment(event.date).local().format('Z')}
</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

suppressHydrationWarning masks the problem rather than fixing it.

These event dates are static data from meetings.json and should render identically on both server and client. The hydration mismatch occurs because line 54 calls .local(), which applies the user's local timezone—causing the server-rendered UTC output to differ from the client-rendered localized output.

Instead of suppressing the warning, fix the root cause by:

  1. Rendering dates in a fixed timezone (UTC) on both server and client, or
  2. Deferring date rendering to client-only using useEffect + useState.

React documentation states that suppressHydrationWarning should only be used for content that is intentionally different between server and client (e.g., real-time timestamps). Static event dates don't qualify.

🔎 Recommended fix: Remove `.local()` for consistent UTC rendering
-              <span className='flex-1 self-center text-center' suppressHydrationWarning>{moment(event.date).format('D')}</span>
+              <span className='flex-1 self-center text-center'>{moment(event.date).utc().format('D')}</span>
             </div>
             <div className='grow text-left sm:mt-0 sm:pl-6'>
               <h2 className='title-font font-medium text-gray-900 hover:text-gray-500'>{event.title}</h2>
-              <p className='text-gray-600' suppressHydrationWarning>
-                {moment(event.date).local().format('LLLL')} UTC
-                {moment(event.date).local().format('Z')}
+              <p className='text-gray-600'>
+                {moment(event.date).utc().format('LLLL')} UTC
               </p>

This ensures both server and client render the same UTC date, eliminating the hydration mismatch.

Alternative: Client-only rendering

If you want to show dates in the user's local timezone, defer rendering to the client:

+const [formattedDates, setFormattedDates] = React.useState<Map<number, {day: string, full: string}>>(new Map());
+
+React.useEffect(() => {
+  const dates = new Map();
+  getEvents(eventsData, size).forEach((event, index) => {
+    dates.set(index, {
+      day: moment(event.date).format('D'),
+      full: `${moment(event.date).local().format('LLLL')} UTC${moment(event.date).local().format('Z')}`
+    });
+  });
+  setFormattedDates(dates);
+}, [size]);

Then use formattedDates.get(index)?.day and formattedDates.get(index)?.full in the JSX.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span className='flex-1 self-center text-center' suppressHydrationWarning>{moment(event.date).format('D')}</span>
</div>
<div className='grow text-left sm:mt-0 sm:pl-6'>
<h2 className='title-font font-medium text-gray-900 hover:text-gray-500'>{event.title}</h2>
<p className='text-gray-600'>
<p className='text-gray-600' suppressHydrationWarning>
{moment(event.date).local().format('LLLL')} UTC
{moment(event.date).local().format('Z')}
</p>
<span className='flex-1 self-center text-center'>{moment(event.date).utc().format('D')}</span>
</div>
<div className='grow text-left sm:mt-0 sm:pl-6'>
<h2 className='title-font font-medium text-gray-900 hover:text-gray-500'>{event.title}</h2>
<p className='text-gray-600'>
{moment(event.date).utc().format('LLLL')} UTC
</p>
🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[error] 49-49: Prettier formatting issue: Replace {moment(event.date).format('D')} with a multi-line version as suggested.

🤖 Prompt for AI Agents
In components/Calendar.tsx around lines 49–56 the hydration mismatch is caused
by using .local() (and suppressHydrationWarning) so server renders UTC while
client renders local time; fix by removing suppressHydrationWarning and
rendering dates in a fixed timezone on both server and client (e.g., format
using UTC via moment.utc(event.date).format(...) or otherwise ensure you call
.utc() instead of .local()), or if you prefer local-time display, defer
formatting to the client by moving date formatting into a useEffect that sets
formatted strings in state and render those client-only values; choose one
approach and remove suppressHydrationWarning so server and client output match.


return (
<span className='countdown-text-gradient block font-extrabold' data-testid='AnnouncementRemainingDays-text'>
<span className='countdown-text-gradient block font-extrabold' data-testid='AnnouncementRemainingDays-text' suppressHydrationWarning>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix Prettier formatting issue.

The pipeline reports a formatting error on this line. Run npm run format or yarn format to apply the required spacing corrections.

🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[error] 32-32: Prettier formatting issue: Replace spacing around elements as suggested.

🤖 Prompt for AI Agents
In components/campaigns/AnnouncementRemainingDays.tsx around line 32, Prettier
flagged a formatting issue on the span tag; run the project formatter (npm run
format or yarn format) to apply the required spacing/format corrections, or
manually reformat the JSX tag to match the project's Prettier settings (correct
spacing between attributes and close the tag consistently) and then re-run the
formatter or commit the formatted file.

Comment on lines +53 to 54
let active = true;
if (!postDate.isValid()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix Prettier/ESLint formatting issue.

The pipeline reports a missing blank line after the variable declarations on line 53. Add a blank line between the let active = true; declaration and the following if statement.

🔎 Proposed fix
 const postDate = moment(post.date);
 
 let active = true;
+
 if (!postDate.isValid()) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let active = true;
if (!postDate.isValid()) {
const postDate = moment(post.date);
let active = true;
if (!postDate.isValid()) {
🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[error] 53-54: Expected blank line after variable declarations and before following statement.

🤖 Prompt for AI Agents
In components/navigation/EventPostItem.tsx around lines 53 to 54, add a blank
line after the variable declaration "let active = true;" so there's an empty
line before the subsequent "if (!postDate.isValid())" statement to satisfy
Prettier/ESLint formatting rules; update the file accordingly and re-run the
formatter/linter to confirm the warning is resolved.

Comment on lines +82 to 84
<span className='ml-4 text-sm font-semibold' data-testid='Event-span' suppressHydrationWarning>
{active ? moment(postDate).format('MMMM D, YYYY') : 'View Recording'}
</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

suppressHydrationWarning masks the problem for static event dates.

The event date from post.date is static data and should render identically on both server and client. The hydration mismatch likely stems from locale-dependent formatting ('MMMM D, YYYY') or timezone handling in moment().

Instead of suppressing the warning, ensure consistent rendering:

  1. Use UTC consistently: moment(postDate).utc().format('MMMM D, YYYY')
  2. Lock locale: Configure moment to use a fixed locale on both server and client
  3. Client-only rendering: Defer to useEffect if you need user-localized dates

Static event dates don't qualify for suppressHydrationWarning per React's guidance—this attribute is intended for intentionally mismatched content like real-time timestamps.

🔎 Recommended fix: Use UTC for consistent rendering
-              <span className='ml-4 text-sm font-semibold' data-testid='Event-span' suppressHydrationWarning>
+              <span className='ml-4 text-sm font-semibold' data-testid='Event-span'>
-                {active ? moment(postDate).format('MMMM D, YYYY') : 'View Recording'}
+                {active ? moment(postDate).utc().format('MMMM D, YYYY') : 'View Recording'}
               </span>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span className='ml-4 text-sm font-semibold' data-testid='Event-span' suppressHydrationWarning>
{active ? moment(postDate).format('MMMM D, YYYY') : 'View Recording'}
</span>
<span className='ml-4 text-sm font-semibold' data-testid='Event-span'>
{active ? moment(postDate).utc().format('MMMM D, YYYY') : 'View Recording'}
</span>
🤖 Prompt for AI Agents
In components/navigation/EventPostItem.tsx around lines 82–84, the span uses
suppressHydrationWarning to hide a server/client mismatch caused by locale or
timezone differences when formatting postDate with moment; remove
suppressHydrationWarning and ensure deterministic rendering by formatting the
date in UTC and a fixed locale on both server and client (e.g., call
moment(postDate).utc().locale('en').format('MMMM D, YYYY')), or if you need
user-localized output defer formatting to the client via useEffect and render a
stable placeholder on the server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Intermittent React hydration crash on macOS when changing responsive viewport (Errors #425, #418, #423)

2 participants