Skip to content

fix: guard localStorage SSR in NotificationCenterProvider and suppress hydration mismatches#302

Merged
MaryammAli merged 2 commits into
BlockDash-Studios:mainfrom
Joyful12-tech:fix/ssr-notification-hydration-290
Jul 16, 2026
Merged

fix: guard localStorage SSR in NotificationCenterProvider and suppress hydration mismatches#302
MaryammAli merged 2 commits into
BlockDash-Studios:mainfrom
Joyful12-tech:fix/ssr-notification-hydration-290

Conversation

@Joyful12-tech

Copy link
Copy Markdown
Contributor

Summary

Fixes the SSR hydration issues reported in #290.

Root causes

  1. NotificationCenterProviderwindow.localStorage was accessed only inside useEffect (correct), but the unreadCount derived from the pre-hydration state was used by NotificationBell to render a badge, causing a hydration mismatch.
  2. NotificationBell — rendered the unread badge count from context before localStorage was read, so the server HTML (count = 3) differed from the initial client render.
  3. NotificationFeed — called formatRelativeTime() (which calls Date.now()) during render; the server timestamp and the client timestamp differ slightly, triggering React's hydration warning.
  4. notifications.tsformatRelativeTime had no fallback if Intl.RelativeTimeFormat is unavailable and no documentation on the SSR hydration caveat.

Changes

File Change
NotificationCenterProvider.tsx Guard localStorage with typeof window !== 'undefined'; add try/catch on persist write; expose hasHydrated in context
NotificationBell.tsx Use displayCount = hasHydrated ? unreadCount : 0 so the badge is always 0 on the server render, matching the initial client render
NotificationFeed.tsx Add suppressHydrationWarning on the <span> containing formatRelativeTime() output
notifications.ts Document pure functions; add try/catch fallback in formatRelativeTime; add JSDoc explaining hydration caveat
vitest.config.ts New — configures jsdom environment and @/* alias for Vitest
vitest.setup.ts New — imports @testing-library/jest-dom matchers
src/__tests__/notifications.test.tsx New — 23 test cases covering sortNotifications, filterNotifications, formatRelativeTime, and NotificationCenterProvider SSR hydration

Testing

# from app/frontend
pnpm vitest run

All 23 cases pass.

Closes #290

Joyful12-tech and others added 2 commits July 16, 2026 15:16
…R hydration mismatches

- NotificationCenterProvider: guard localStorage access with typeof window check,
  add try/catch on persist, expose hasHydrated in context value so consumers can
  opt-in to hydration-safe rendering
- NotificationBell: use displayCount = hasHydrated ? unreadCount : 0 so the
  unread badge is always 0 on the server render, matching the initial client
  render and eliminating the hydration mismatch
- NotificationFeed: add suppressHydrationWarning on the timestamp span that
  calls formatRelativeTime() — the function is pure/SSR-safe but Date.now()
  produces different values between server and client
- notifications.ts: document sortNotifications and filterNotifications as pure
  (no browser globals); add try/catch fallback in formatRelativeTime for
  environments without Intl.RelativeTimeFormat; add JSDoc explaining hydration
  caveat for callers
- Add vitest.config.ts + vitest.setup.ts for jsdom-based unit tests
- Add src/__tests__/notifications.test.tsx covering sortNotifications (5 cases),
  filterNotifications (7 cases), formatRelativeTime (5 cases), and
  NotificationCenterProvider SSR hydration behaviour (6 cases)

Closes BlockDash-Studios#290

@MaryammAli MaryammAli left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@MaryammAli
MaryammAli merged commit 3aad934 into BlockDash-Studios:main Jul 16, 2026
1 check passed
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.

NotificationCenterProvider uses window.localStorage without guarding SSR hydration

2 participants