Prevent Hydration Mismatch Without Flickering
Install this skill
npx skills add vercel-labs/agent-skillsWorks across Claude Code, Cursor, Codex, Copilot & Antigravity
Hydration mismatches occur when server-rendered HTML fails to match the initial client-side render, often due to accessing browser-specific APIs like localStorage before the component mounts. Simply relying on React's useEffect to apply user preferences leads to a noticeable visual flash as the component re-renders with the correct state. This skill resolves these issues by inserting a synchronous, blocking script directly after the container element. By executing this script before the browser finishes painting, you update the DOM state immediately. This method bypasses the standard React lifecycle for these initial style updates, ensuring the user sees the intended theme or preference without any layout shift or inconsistencies during the hydration phase. It creates a stable, performant experience for any UI element dependent on persisted browser storage.
When to Use This Skill
- •Applying saved dark or light mode preferences on page load
- •Restoring authenticated UI states based on browser-stored tokens
- •Setting font size preferences or accessibility overrides immediately
- •Localizing display settings stored in persistent browser storage
- •Initializing complex dashboard layouts based on cached user configurations
How to Invoke This Skill
Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:
- “Stop dark mode flash on page load
- “Fix hydration mismatch for user theme
- “How to apply localStorage state before React renders
- “Prevent visual flicker during hydration
- “Synchronous DOM updates for server-side rendered components
What this skill does
- •Injects synchronous scripts to modify DOM nodes before React hydration
- •Bypasses flicker-inducing layout shifts caused by delayed useEffect updates
- •Prevents server-side errors caused by accessing undefined client APIs
- •Standardizes CSS class or attribute application during the first paint
- •Maintains zero-dependency, vanilla JavaScript compatibility
When not to use it
- ✕When the state data originates from a server-side API or database
- ✕When simple CSS variables can handle the logic via media queries
- ✕When the UI component must remain strictly declarative without raw DOM manipulation
Example workflow
- Identify the UI element that requires a stored user preference
- Remove existing useEffect hooks responsible for updating that state
- Wrap the target component in a parent div with a unique ID
- Append a dangerouslySetInnerHTML script block immediately following the element
- Write the script to fetch the storage value and modify the element class directly
- Verify the page renders the correct state on the first paint
Prerequisites
- –React or Next.js framework environment
- –Basic understanding of the DOM rendering lifecycle
Pitfalls & limitations
- !Requires manual maintenance of DOM IDs to target elements
- !Bypassing React state makes the UI partially non-declarative
- !Script injection must be placed precisely to ensure execution timing
FAQ
How it compares
While manual useEffect methods cause visual flickering, this approach uses blocking scripts to manipulate the DOM during the paint cycle, ensuring the UI is correct on the first frame without extra framework overhead.
📄 Full skill instructions — original source: vercel-labs/agent-skills
When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.
**Incorrect (breaks SSR):**
function ThemeWrapper({ children }: { children: ReactNode }) {
// localStorage is not available on server - throws error
const theme = localStorage.getItem('theme') || 'light'
return (
<div className={theme}>
{children}
</div>
)
}Server-side rendering will fail because
localStorage is undefined.**Incorrect (visual flickering):**
function ThemeWrapper({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState('light')
useEffect(() => {
// Runs after hydration - causes visible flash
const stored = localStorage.getItem('theme')
if (stored) {
setTheme(stored)
}
}, [])
return (
<div className={theme}>
{children}
</div>
)
}Component first renders with default value (
light), then updates after hydration, causing a visible flash of incorrect content.**Correct (no flicker, no hydration mismatch):**
function ThemeWrapper({ children }: { children: ReactNode }) {
return (
<>
<div id="theme-wrapper">
{children}
</div>
<script
dangerouslySetInnerHTML={{
__html:
(function() {
try {
var theme = localStorage.getItem('theme') || 'light';
var el = document.getElementById('theme-wrapper');
if (el) el.className = theme;
} catch (e) {}
})();
,
}}
/>
</>
)
}The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch.
This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values.
How to Use This Skill Unit
Option A: Project-Specific (Recommended)
- Click "Download" above
- In your project, create the directory:
.agent/skills/rendering-hydration-no-flicker/ - Save the file as
SKILL.md - The agent will automatically discover the skill based on its description.
Option B: Global Installation (All Agents)
Save the file to these locations to make it available across all projects:
- Claude Code:
~/.claude/skills/vercel-labs/agent-skills/rendering-hydration-no-flicker/SKILL.md - Cursor:
~/.cursor/skills/vercel-labs/agent-skills/rendering-hydration-no-flicker/SKILL.md - Antigravity:
~/.gemini/antigravity/skills/vercel-labs/agent-skills/rendering-hydration-no-flicker/SKILL.md
🚀 Install with CLI:npx skills add vercel-labs/agent-skills