Skip to content

Commit 791b4a6

Browse files
authored
fix: resolve Sentry hydration mismatch and suppress extension noise (#439)
1 parent 699ae20 commit 791b4a6

File tree

2 files changed

+43
-3
lines changed

2 files changed

+43
-3
lines changed
Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
11
'use client';
22
import { motion, useReducedMotion } from 'framer-motion';
3-
import { ReactNode } from 'react';
3+
import { ReactNode, useState, useEffect, memo } from 'react';
44

5-
export function FadeIn({ children, delay = 0, className = '' }: { children: ReactNode; delay?: number; className?: string }) {
5+
export const FadeIn = memo(function FadeIn({ children, delay = 0, className = '' }: { children: ReactNode; delay?: number; className?: string }) {
66
const shouldReduce = useReducedMotion();
7+
const [isMounted, setIsMounted] = useState(false);
8+
9+
// Defer Framer Motion's initial state until after hydration. Without this,
10+
// Framer Motion applies initial={{ opacity: 0 }} before React hydrates, causing
11+
// the server-rendered HTML (opacity:1) to mismatch the client DOM (opacity:0).
12+
useEffect(() => {
13+
setIsMounted(true);
14+
}, []);
15+
16+
if (!isMounted) {
17+
// Server + hydration pass: render without Framer Motion so the DOM matches
18+
// the server HTML exactly. No opacity/transform styles applied.
19+
return <div className={className}>{children}</div>;
20+
}
21+
722
return (
823
<motion.div
924
initial={{ opacity: 0, y: shouldReduce ? 0 : 16 }}
@@ -14,4 +29,4 @@ export function FadeIn({ children, delay = 0, className = '' }: { children: Reac
1429
{children}
1530
</motion.div>
1631
);
17-
}
32+
});

website/src/instrumentation-client.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,31 @@ Sentry.init({
1212
integrations: [
1313
Sentry.replayIntegration(),
1414
],
15+
beforeSend(event) {
16+
const msg = event.exception?.values?.[0]?.value ?? "";
17+
18+
// Drop known browser-extension noise that cannot be fixed in app code:
19+
//
20+
// 1. "Cannot assign to read only property 'pushState'" — extensions
21+
// (Vue/Redux DevTools, privacy tools) wrap history.pushState before our
22+
// app loads, making it read-only. Not reproducible without the extension.
23+
//
24+
// 2. "Object Not Found Matching Id:N, MethodName:update" — Chrome DevTools
25+
// Protocol messages from extensions interacting with CodeMirror/Monaco.
26+
// The error originates outside our code and only affects a single session.
27+
const isExtensionNoise =
28+
msg.includes("Cannot assign to read only property 'pushState'") ||
29+
msg.includes("Object Not Found Matching Id:");
30+
31+
if (isExtensionNoise) {
32+
if (process.env.NODE_ENV === "development") {
33+
console.debug("[Sentry] Suppressed extension noise:", msg);
34+
}
35+
return null;
36+
}
37+
38+
return event;
39+
},
1540
});
1641

1742
export const onRouterTransitionStart = dsn

0 commit comments

Comments
 (0)