AniUI Academy

React 19.3 can replace your mounted-state workaround

Still setting mounted state in an Effect before reading localStorage? React 19.3 offers a browser-only rendering API, with a server-rendering trade-off.

Anish Lawrence3 min read

You need to read a saved draft from localStorage. Suddenly, your component has mounted state, an Effect, and a conditional return just to reach browser-only code.

React 19.3 gives that particular workaround a more direct alternative: use(browser()).

Meet browser-only rendering

React 19.3 introduced browser() on September 9, 2026. Paired with use, it lets a component opt out of server rendering.

Import use from React and browser from react-dom. Inside your component, call use(browser()) before the code that requires the browser.

During server rendering, React stops rendering that component and includes the nearest Suspense boundary’s fallback instead. In the browser, the call does not suspend, so rendering continues.

You can optionally pass browser() a reason explaining why the component needs browser-only rendering. That makes the decision easier to understand when you revisit it.

Calling browser() by itself does not change rendering. Passing its result to use is what makes the pattern work.

Replace the mounted flag

For browser-dependent initial rendering, use(browser()) can replace an Effect whose only job is setting mounted state.

The familiar workaround starts with mounted set to false. An Effect changes it to true, and the component returns a placeholder until that happens.

That approach coordinates an extra state update just to establish which environment is rendering the component. With use(browser()), you express that requirement directly.

For a saved-draft editor, place the call before reading localStorage in your state initializer. Give the surrounding Suspense boundary a useful placeholder, such as “Loading saved draft.”

This does not replace the editor’s other responsibilities. Saving changes, handling unavailable storage, and deciding when to discard a draft still need their own logic.

Client still means server

The “use client” directive marks a Client Component boundary. It does not guarantee that the component skips server rendering.

That distinction matters in applications using Server Components. A Client Component can still participate in producing the initial HTML before React makes it interactive in the browser.

Adding the directive therefore does not make browser APIs available during server rendering. Nor does a different return value behind a window check automatically produce matching server and client output.

use(browser()) makes the server-rendering decision explicit. In a Server Components application, call it from a Client Component and put a Suspense boundary above the component that needs it.

Keep that boundary close enough that an unavailable draft editor does not hide the rest of the page.

Know what you lose

Opting out of server rendering means that region contributes a fallback to the initial HTML instead of its actual content.

That can be reasonable for an editor restoring a private draft from the current browser. The server cannot read that browser’s localStorage anyway.

It is a weaker default for an article, product description, or navigation that could already produce useful HTML. Those regions should not wait for browser JavaScript without a concrete reason.

This is also not a general fix for hydration errors. Investigate unexpected mismatches before deciding to skip server rendering.

One practical limitation: a call inside your component cannot protect browser-only code that already executes when a module is imported. Dependencies that access window at module scope still need separate handling.

Choose the smallest region

Use browser-only rendering for the smallest component that needs it. Preserve useful server-rendered content around that component.

Start with a saved-draft editor or a device-specific panel. Check the initial fallback, the final browser output, and the experience on a slow connection.

Also test failure separately from waiting. Suspense provides a loading fallback; ordinary rendering failures need an appropriate Error Boundary. Storage operations may need their own recovery behavior too.

My take: replacing a mounted flag is worthwhile when it clarifies why a region cannot render on the server. Treat use(browser()) as an explicit rendering choice, rather than a default wrapper for every Client Component.

Thanks for reading. #React #NextJS #FrontendDevelopment #WebDevelopment

Related reading

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.