--- title: "React's browser() API: Opt Out of SSR Without the Mounted-State Hack" author: "Danny Holloran" published: "2026-11-10T11:12:42.326Z" updated: "2026-11-10T11:12:42.326Z" url: "https://zyvop.com/react-s-browser-api-opt-out-of-ssr-without-the-mounted-state-hack-hfboo" canonical: "https://danholloran.me/posts/reacts-browser-api-opt-out-of-ssr-without-the-mounted-state-hack" tags: ["react.js","react","JavaScript","performance"] --- # React's browser() API: Opt Out of SSR Without the Mounted-State Hack ![Cover image]() Every server-rendered React app eventually grows a component that just can't render on the server. It reads a draft from `localStorage`, shows the visitor's time zone, or wraps a charting library that touches `window` on import. So you reach for the usual workaround: a `mounted` flag set in an effect, or a `typeof window !== 'undefined'` check sprinkled through render. Both work, sort of. The effect version renders nothing (or a placeholder) on the first client pass and then flips, which costs you an extra render and some layout shift. The `typeof window` version is worse, because it renders different output on the server and the client, which is exactly how you get a hydration mismatch. React 19.3, released in September 2026, finally ships a real answer: `use(browser())`. ## What `browser()` actually does `browser` is a new export from `react-dom`. You call it inside `use`, and it marks the component as browser-only during server rendering: ```jsx import { Suspense, use, useState } from "react"; import { browser } from "react-dom"; function SavedDraft() { use(browser("The draft is stored in localStorage.")); const [draft, setDraft] = useState(() => localStorage.getItem("draft") ?? ""); return (