A provider that only forwards a value. A wrapper added only to hold a ref. A loading state that flashes before the real content arrives. React 19.3 gives you better ways to handle these small but recurring pieces of interface work.
This guide focuses on what this release adds to the library, with examples you can reason through before changing a project. For state-preserving panels, Effect Events, and profiling, see the separate React 19.2 feature guide.
What does React 19.3 add?
React 19.3 stabilizes View Transitions and Fragment refs, adds browser(), supports direct client Context providers in Server Components, and integrates with Trusted Types. The React team released it on September 9, 2026.
ViewTransition
Animate eligible UI changes.
Fragment refs
Control sibling elements without adding a wrapper.
browser()
Defer browser-dependent content to the client.
Direct Context in RSC
Provide values without a separate client wrapper.
Trusted Types support
Preserve trusted values from your security policies when passing them to the DOM.
The rest of this article connects those additions to practical choices. The code uses JavaScript with JSX. In a framework using React Server Components (RSC), Server Components execute outside the browser, while Client Components support browser interactivity. Client Components can still have their initial HTML rendered on the server. Interactive samples belong in client modules, and the new APIs require a compatible React 19.3 integration.
Render Context directly in a Server Component
This is a useful cleanup for applications that get data on the server and share it with client-side controls. Context is React’s way to make a value available to descendants without passing it through every intermediate component.
Before 19.3, an RSC setup needed a Client Component wrapper to forward that value into a client Context. In 19.3, the Server Component can import the Context and render its provider directly. The Server Components reference documents the new boundary.
Before 19.3
- Server ComponentLayoutPasses a value
- Client moduleProvider wrapperForwards the value
- Client moduleContextProvides the value
- Client ComponentConsumerReads the value
React 19.3
- Server ComponentLayoutPasses a value
- Imported client ContextContextRendered directly by the layout
- Client ComponentConsumerReads the value
The Context definition stays in a 'use client' module. Server Components can provide serializable data; they cannot read context.
Keep the Context definition in a client module
In this teaching example, a course heading reads course metadata. Both the Context definition and its consumer stay in the client module:
// course-context.jsx
"use client";
import { createContext, useContext } from "react";
export const CourseContext = createContext(null);
export function CourseHeading() {
const course = useContext(CourseContext);
return <h2>{course?.name ?? "Choose a course"}</h2>;
}Before: a wrapper whose only job is forwarding
// course-provider.jsx: the forwarding wrapper
"use client";
import { CourseContext } from "./course-context";
export function CourseProvider({ course, children }) {
return (
<CourseContext value={course}>
{children}
</CourseContext>
);
}
// A Server Component would use:
// <CourseProvider course={course}>
// <CourseHeading />
// </CourseProvider>After: the server renders the imported Context
// CoursePage.jsx: a Server Component in React 19.3
import { CourseContext, CourseHeading } from "./course-context";
export default function CoursePage() {
const course = { id: "react-basics", name: "React basics" };
return (
<CourseContext value={course}>
<CourseHeading />
<p>Start with props, state, and small components.</p>
</CourseContext>
);
}The benefit is one less forwarding component. A course toolbar, progress indicator, and client-side lesson menu could read the same course value. Keep using ordinary props when only one child needs it.
The shorter <Context value={...}> provider syntax was already available in React 19. What’s new here is rendering an imported client Context directly from a Server Component, as distinct from the provider syntax itself.
The boundary still matters. Server Components do not gain the ability to create or consume client Context. Values crossing into client code must use React-supported serializable types. Our course object is suitable; an ordinary callback closure is not. Never send private server data merely because it is easy to place in Context.
Keep provider wrappers that manage client state, event handlers, or other behavior. This example removes a wrapper that only forwards a value. It does not remove the need for stateful providers.
View Transitions: help people follow a change
Imagine opening a lesson preview. The card expands, text appears, and nearby content moves. A short visual transition can help the reader connect the preview to the button they just pressed.
ViewTransition coordinates that change with the browser’s View Transition API. Wrap the changing content, then make an eligible update, such as a state change inside startTransition. An ordinary urgent state update won’t activate this animation.
"use client";
import { startTransition, useState, ViewTransition } from "react";
export default function LessonPreview() {
const [expanded, setExpanded] = useState(false);
return (
<>
<button
aria-expanded={expanded}
aria-controls="lesson-details"
onClick={() => startTransition(() => {
setExpanded(value => !value);
})}
>
{expanded ? "Hide lesson" : "Preview lesson"}
</button>
<ViewTransition>
<div id="lesson-details">
{expanded && <p>Learn props by building a profile card.</p>}
</div>
</ViewTransition>
</>
);
}Here, expanded remembers whether the lesson is open. The click schedules its change as a Transition. The boundary lets React coordinate a visual update around the details container. The default visual treatment is a cross-fade; you can customize it with CSS.
- 01 / INPUT
Respond immediately
Update the input value as the person types.
- 02 / RENDER
Mark less urgent work
startTransitionmarks the results update as non-urgent. - 03 / PRESENT
Connect the two views
ViewTransitionanimates the eligible visual change.
startTransition; see the scheduling docs and ViewTransition docs.Where it helps: a list opening into a detail view, a changing gallery, or a lesson card revealing more information. Keep quick button hovers in CSS. For typing, update the controlled input immediately; you can defer the expensive results separately. React’s transition guidance explains this priority distinction.
For a carousel, addTransitionType can label a Transition as “next” or “previous” so the same content change uses the appropriate animation direction. Start with the basic boundary before adding that layer.
Respect reduced motion. React does not disable these animations automatically. Add a reduced-motion rule and check the result with that preference enabled.
/* Put this in your global stylesheet. */
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}This feature currently targets the DOM. Check your supported browsers and keep the interface usable when the animation is unavailable.
Coordinate loading states with View Transitions
Suspense displays a fallback while supported work waits. React 19.3’s transition integration lets you coordinate the change from that fallback to the loaded content. A lesson skeleton becoming a finished card is a good use case.
<ViewTransition default="none" update="auto">
<Suspense fallback={<LessonSkeleton />}>
<Lesson />
</Suspense>
</ViewTransition>This configuration opts into update animation while leaving the boundary’s appearance and removal unanimated. Lesson must actually suspend through a supported loading mechanism. Wrapping a fetch started in an ordinary Effect does not make it Suspense-aware.
The Suspense documentation explains how Suspense can coordinate stylesheet loading. During a View Transition update, React can additionally wait for visible images and new fonts, up to a timeout. This can reduce the visual jump when a card’s text arrives before its image. An image with an onLoad handler opts out of that image-waiting behavior.
Try it on a slow connection. The fallback should be useful immediately, and the final reveal should remain understandable with reduced motion enabled. The goal is a coherent loading state, not an animation on every change.
Fragment refs: work with a group without a wrapper
A Fragment groups siblings without adding an HTML element. Previously, attaching behavior to that group could mean introducing a wrapper just to hold a ref. With Fragment refs, you can target the group while leaving its DOM structure intact.
"use client";
import { Fragment, useRef } from "react";
export default function ProfileFields() {
const fields = useRef(null);
return (
<>
<button onClick={() => fields.current?.focus()}>
Start editing
</button>
<Fragment ref={fields}>
<label>
Display name <input name="displayName" />
</label>
<label>
Bio <textarea name="bio" />
</label>
</Fragment>
</>
);
}Pressing “Start editing” moves focus to the first focusable descendant. That makes sense here because the user explicitly requested it. Use the full <Fragment> form: the <> shorthand cannot take a ref.
Where it helps: focus management across related fields, visibility observation, or adding behavior around a component library. A Fragment ref gives you a FragmentInstance with supported methods, not an extra element with a style property. Keep a real wrapper when it provides useful semantics or layout.
browser(): isolate the part that needs the browser
Your server can render an article, but it cannot read a visitor’s navigator.language. React DOM’s browser(), consumed with use(browser()), lets a component wait for the browser. A surrounding Suspense fallback appears in the server HTML.
"use client";
import { Suspense, use } from "react";
import { browser } from "react-dom";
function DeviceLanguage() {
use(browser());
return <p>Browser language: {navigator.language}</p>;
}
export default function LanguageCard() {
return (
<Suspense fallback={<p>Checking browser language…</p>}>
<DeviceLanguage />
</Suspense>
);
}The fallback gives this small region a readable loading state.
Illustrative output. The actual value comes from the visitor’s browser.
Where it helps: a browser-language widget, a local-draft panel, or a browser-only library. Keep the Suspense boundary above the component that calls use(browser()). Calling browser() alone is insufficient.
In an RSC application this belongs in a Client Component. Keep the surrounding public content available from the server. Moving an entire page behind a loading fallback would give readers less useful initial HTML.
Libraries that access browser APIs while being imported may still need client-side dynamic loading. This API controls component rendering; it does not change how a static import is evaluated.
Trusted Types: preserve the browser’s security contract
React 19.3 stops converting Trusted Types objects back into plain strings before passing them to relevant DOM APIs. That matters for applications enforcing Trusted Types through a Content Security Policy. The release notes describe this compatibility change.
A Trusted Types policy produces objects such as TrustedHTML. The application supplies that policy and its sanitization logic. React does not decide whether arbitrary HTML is safe.
- 01Input
Treat incoming HTML as untrusted.
- 02Your policy
Validate and sanitize for the intended use.
- 03Typed output
The policy produces a trusted object.
- 04React DOM
Preserve the object rather than stringify it.
- 05The browser
Enforce the configured policy at covered sinks.
Where it helps: an application with a deliberately configured HTML rendering pipeline, such as a content editor. If you are displaying ordinary text, keep using JSX’s normal text rendering. Don’t add raw HTML just to try this feature.
Adopt React 19.3 without changing everything at once
First check the versions your app uses. Frameworks that integrate Server Components must support the release’s underlying APIs, so follow their compatibility guidance before changing React independently.
npm ls react react-domFor a compatible project managing React directly, this selects the latest available 19.3 patch for both packages. Review the resolved versions and lockfile:
npm install react@~19.3.0 react-dom@~19.3.0- Start from a working baseline. Run existing checks and keep the original lockfile available for comparison.
- Check framework, types, and lint support. TypeScript projects need declarations for the APIs they use. Keep Hooks linting enabled.
- Choose a concrete improvement. Remove one forwarding provider or add a transition to one detail view. Leave unrelated components alone.
- Test the awkward paths. Reload a server-rendered route, check hydration warnings, navigate by keyboard, and enable reduced motion.
- Build for production. A working development view is only one part of an upgrade check.
React 18 projects should first follow the React 19 major-version upgrade guide. Check the versions page when using these instructions in the future.
Try a course-detail page with a clear finish line
Build only what the page needs
- Provide sample course metadata from a Server Component to two client consumers. Explain which code belongs in each module.
- Remove a provider wrapper only if it does nothing beyond forwarding that metadata.
- Add a lesson preview using ViewTransition. Verify that the button and content still work with animations disabled.
- Add a browser-language widget inside its own Suspense boundary. Keep the course description in the initial HTML.
- If you use a Fragment ref for focus, explain why a normal element ref or semantic fieldset would not be the better choice.
You do not need a Trusted Types policy for this exercise’s ordinary text. Add security infrastructure when your application’s rendering requirements call for it.
That leaves you with a small, explainable project. Each new API has a reason to be there, and you can show what improved.
Common React 19.3 questions
What are the main new features in React 19.3?
React 19.3 stabilizes View Transitions and Fragment refs, adds browser() for selective client rendering, supports direct rendering of client Context providers in Server Components, and integrates with Trusted Types.
Can a Server Component read Context in React 19.3?
No. It can render a Context provider imported from a client module and pass a serializable value to client consumers. This does not let Server Components call useContext or create client Context themselves.
Was the shorter Context provider syntax introduced in 19.3?
No. Rendering <Context value={...}> instead of <Context.Provider> was introduced in React 19. React 19.3 removes an extra client provider wrapper when a Server Component renders an imported client Context.
Does startTransition automatically animate an update?
No. It marks non-urgent rendering work. ViewTransition coordinates eligible visual changes with the browser. Keep controlled input updates immediate and handle reduced-motion preferences explicitly.
Does Trusted Types support make user HTML safe?
No. React preserves the trusted objects produced by a policy. Your application still needs a correctly designed sanitization policy and enforcement. Do not treat untrusted strings as safe HTML.
Sources and further reading
- React 19.3 announcementThe React Team, September 9, 2026. Release scope and DOM compatibility changes.
- Rendering a Context provider in a Server ComponentDirect rendering, client declaration, and consumer boundaries.
- createContext and use clientProvider syntax and supported server-to-client values.
- ViewTransition, addTransitionType, and SuspenseUpdate eligibility, animation intent, and coordinated loading.
- Fragment refs and browser()Methods, constraints, and selective browser rendering.
- MDN: Trusted Types APIPolicy responsibilities and browser enforcement.
Claps, saves, topic follows, and comment previews are for this visit only. Comments are not published.
