diff --git a/package.json b/package.json
index 324f171d2..23f0c1ccd 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
"@babel/preset-react": "7.28.5",
"@mdx-js/mdx": "^2.1.3",
"@resvg/resvg-js": "^2.6.2",
+ "@shuding/opentype.js": "1.4.0-beta.0",
"@types/body-scroll-lock": "^2.6.1",
"@types/classnames": "^2.2.10",
"@types/debounce": "^1.2.1",
diff --git a/public/images/team/gh-rh389.jpg b/public/images/team/gh-robhogan.jpg
similarity index 100%
rename from public/images/team/gh-rh389.jpg
rename to public/images/team/gh-robhogan.jpg
diff --git a/scripts/generateOgImages.mjs b/scripts/generateOgImages.mjs
index 937727a1c..bc805d386 100644
--- a/scripts/generateOgImages.mjs
+++ b/scripts/generateOgImages.mjs
@@ -14,6 +14,7 @@ import path from 'path';
import satori from 'satori';
import {Resvg} from '@resvg/resvg-js';
import matter from 'gray-matter';
+import opentype from '@shuding/opentype.js';
const ROOT = process.cwd();
const CONTENT_DIR = path.join(ROOT, 'src', 'content');
@@ -34,6 +35,33 @@ const medium = fs.readFileSync(
path.join(ROOT, 'public', 'fonts', 'Optimistic_Display_W_Md.ttf')
);
+// Title area width: card width minus the horizontal padding (80px per side).
+const TITLE_MAX_WIDTH = 1200 - 80 * 2;
+const TITLE_MAX_FONT = 96;
+const TITLE_MIN_FONT = 56;
+// Small margin so borderline titles don't wrap and orphan a single trailing
+// character (e.g. the "p" in "renderToStaticMarkup", which is 1px too wide
+// to fit on one line at 96px).
+const TITLE_SAFETY = 8;
+
+const boldFont = opentype.parse(
+ bold.buffer.slice(bold.byteOffset, bold.byteOffset + bold.byteLength)
+);
+
+// Multi-word titles wrap cleanly at spaces, so a length bucket is fine for
+// them. Single-word titles (most API names) can only break mid-word, which
+// leaves an orphaned letter on its own line, so instead shrink them just
+// enough to fit on a single line.
+function titleFontSize(title) {
+ const trimmed = title.trim();
+ if (/\s/.test(trimmed)) {
+ return trimmed.length > 24 ? 72 : 96;
+ }
+ const widthPerPx = boldFont.getAdvanceWidth(trimmed, 1);
+ const fit = Math.floor((TITLE_MAX_WIDTH - TITLE_SAFETY) / widthPerPx);
+ return Math.max(TITLE_MIN_FONT, Math.min(TITLE_MAX_FONT, fit));
+}
+
function el(type, style, children) {
return {type, props: {style, children}};
}
@@ -103,7 +131,7 @@ function card(title, pagePath) {
flexGrow: 1,
display: 'flex',
alignItems: 'center',
- fontSize: title.length > 24 ? 72 : 96,
+ fontSize: titleFontSize(title),
fontFamily: 'Optimistic Display Bold',
color: '#f6f7f9',
lineHeight: 1.1,
diff --git a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md
index dbd673a29..95821fc43 100644
--- a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md
+++ b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md
@@ -1245,8 +1245,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -2442,8 +2442,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -3670,8 +3670,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -4879,8 +4879,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -6195,8 +6195,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -7493,8 +7493,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -8814,8 +8814,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -10155,8 +10155,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -11441,8 +11441,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -12840,8 +12840,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
@@ -14178,8 +14178,8 @@ root.render(
```json package.json hidden
{
"dependencies": {
- "react": "canary",
- "react-dom": "canary",
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
"react-scripts": "latest"
},
"scripts": {
diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md
index 2e95e24ea..f3ae269bb 100644
--- a/src/content/community/conferences.md
+++ b/src/content/community/conferences.md
@@ -10,27 +10,41 @@ Do you know of a local React.js conference? Add it here! (Please keep the list c
## Upcoming Conferences {/*upcoming-conferences*/}
-### React Paris 2026 {/*react-paris-2026*/}
-March 26 - 27, 2026. In-person in Paris, France (hybrid event)
+### ZurichJS Conf 2026 {/*zurichjs-conf-2026*/}
+September 10-11, 2026. In-person in Zurich, Switzerland
-[Website](https://react.paris/) - [Twitter](https://x.com/BeJS_)
+[Website](https://conf.zurichjs.com?utm_campaign=ZurichJS_Conf&utm_source=referral&utm_content=reactjs_community_conferences) - [Twitter](https://x.com/zurichjs) - [LinkedIn](https://www.linkedin.com/company/zurichjs/)
-### CityJS London 2026 {/*cityjs-london-2026*/}
-April 14-17, 2026. In-person in London
+### React Alicante 2026 {/*react-alicante-2026*/}
+Sep 24 - 26, 2026. In-person in Alicante
-[Website](https://india.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)
+[Website](https://reactalicante.es//) - [Twitter](https://x.com/ReactAlicante)
-### ZurichJS Conf 2026 {/*zurichjs-conf-2026*/}
-September 10-11, 2026. In-person in Zurich, Switzerland
+### CityJS Athens 2026 {/*cityjs-athens-2026*/}
+October 21-23, 2026. In-person in Athens
+
+[Website](https://athens.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)
+
+
+## Past Conferences {/*past-conferences*/}
-[Website](https://conf.zurichjs.com?utm_campaign=ZurichJS_Conf&utm_source=referral&utm_content=reactjs_community_conferences) - [Twitter](https://x.com/zurichjs) - [LinkedIn](https://www.linkedin.com/company/zurichjs/)
### React Conf Japan 2027 {/*react-conf-japan-2027*/}
April 24, 2027. In-person in Tokyo, Japan
[Website](https://reactconf.jp/) - [Twitter](https://x.com/reactconfjp)
-## Past Conferences {/*past-conferences*/}
+### CityJS London 2026 {/*cityjs-london-2026*/}
+April 14-17, 2026. In-person in London
+
+[Website](https://india.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)
+
+
+### React Paris 2026 {/*react-paris-2026*/}
+March 26 - 27, 2026. In-person in Paris, France (hybrid event)
+
+[Website](https://react.paris/) - [Twitter](https://x.com/BeJS_)
+
### CityJS New Delhi 2026 {/*cityjs-newdelhi-2026*/}
February 12-13, 2026. In-person in New Delhi, India
diff --git a/src/content/community/team.md b/src/content/community/team.md
index e321f240b..4e307463d 100644
--- a/src/content/community/team.md
+++ b/src/content/community/team.md
@@ -26,7 +26,7 @@ React work is organized into working groups, each responsible for an area of the
-
+
Ricky majored in theoretical math and somehow found himself on the React Native team for a couple years before joining the React team. When he's not programming you can find him snowboarding, biking, climbing, golfing, or closing GitHub issues that do not match the issue template.
@@ -38,10 +38,12 @@ React work is organized into working groups, each responsible for an area of the
## Working Group members {/*working-group-members*/}
-
+
+
+
@@ -77,16 +79,16 @@ React work is organized into working groups, each responsible for an area of the
- Mike went to grad school dreaming of becoming a professor but realized that he liked building things a lot more than writing grant applications. Mike joined Meta to work on Javascript infrastructure, which ultimately led him to work on the React Compiler. When not hacking on either Javascript or OCaml, Mike can often be found hiking or skiing in the Pacific Northwest.
+ Mike went to grad school dreaming of becoming a professor but realized that he liked building things a lot more than writing grant applications. Mike joined Meta to work on JavaScript infrastructure, which ultimately led him to work on the React Compiler. When not hacking on either Javascript or OCaml, Mike can often be found hiking or skiing in the Pacific Northwest.
-
+
-
+
-
+
Ruslan's introduction to UI programming started when he was a kid by manually editing HTML templates for his custom gaming forums. Somehow, he ended up majoring in Computer Science. He enjoys music, games, and memes. Mostly memes.
@@ -102,8 +104,6 @@ React work is organized into working groups, each responsible for an area of the
-
-
## Advisors {/*advisors*/}
@@ -115,7 +115,7 @@ React work is organized into working groups, each responsible for an area of the
- Like many others, Jimmy started programming with the hopes of being able to work in the gaming industry. Fast forward a few years, he somehow decided that React and Javascript were pretty fun and that helping other developers build fast experiences was a more interesting life goal. After starting his career at Meta, working on product infrastructure and (briefly) on React Native, Jimmy now works at Vercel, where he helps his team build Next.js. He sadly does not get much time for video games anymore.
+ Like many others, Jimmy started programming with the hopes of being able to work in the gaming industry. Fast forward a few years, he somehow decided that React and JavaScript were pretty fun and that helping other developers build fast experiences was a more interesting life goal. After starting his career at Meta, working on product infrastructure and (briefly) on React Native, Jimmy now works at Vercel, where he helps his team build Next.js. He sadly does not get much time for video games anymore.
diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md
new file mode 100644
index 000000000..98fa2d465
--- /dev/null
+++ b/src/content/reference/react-dom/browser.md
@@ -0,0 +1,471 @@
+---
+title: browser
+version: canary
+---
+
+
+
+
+
+**The `browser` API is currently only available in React’s Canary and Experimental channels.**
+
+[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
+
+
+
+`browser` lets you mark a component as browser-only during server rendering.
+
+```js
+use(browser(reason?))
+```
+
+
+
+
+
+---
+
+## Reference {/*reference*/}
+
+### `browser(reason?)` {/*browser*/}
+
+Call `browser` inside [`use`](/reference/react/use) to mark a component as browser-only during server rendering:
+
+```js
+import { use } from 'react';
+import { browser } from 'react-dom';
+
+function BrowserOnly() {
+ use(browser('This component requires browser APIs.'));
+ return ;
+}
+```
+
+During server rendering, `use(browser())` stops rendering the component and leaves the closest [``](/reference/react/Suspense) boundary's fallback in its place. In the browser, `use(browser())` returns `undefined`, so the component renders normally.
+
+[See more examples below.](#usage)
+
+#### Parameters {/*parameters*/}
+
+* **optional** `reason`: A string or function that explains why the content needs to render in the browser. The string or the function's return value becomes the `cause` of the `Error` passed to [`onBrowserBailout`](#reporting-browser-only-rendering-on-the-server). React calls a reason function each time a server renderer encounters the value returned by `browser`, but does not call it in the browser. If creating the reason is expensive, pass a function such as `() => new Error(...)`.
+
+#### Returns {/*returns*/}
+
+`browser` returns an opaque value that you can pass to `use` in a component or use as the reason when [aborting a server render](#aborting-pending-server-rendering-for-the-browser). In the browser, passing this value to `use` returns `undefined`.
+
+#### Caveats {/*caveats*/}
+
+* `use(browser())` must be inside a `` boundary during server rendering. Without one, the server render fails.
+* In a React Server Components app, `use(browser())` must be called from a [Client Component](/reference/rsc/use-client), not a [Server Component](/reference/rsc/server-components).
+* Calling `browser()` by itself has no effect. To mark a component as browser-only, pass the value returned by `browser` to `use`. Do not throw it.
+
+---
+
+## Usage {/*usage*/}
+
+### Rendering content only in the browser {/*rendering-content-only-in-the-browser*/}
+
+Call `browser` inside `use` in a component that should only render in the browser:
+
+You can use this instead of checking `typeof window`, waiting for an [`Effect`](/reference/react/useEffect) to set mounted state, or using a framework option to disable server rendering.
+
+Click **Reload** to see the loading fallback in the initial HTML. After hydration, React displays the draft loaded from `localStorage`.
+
+
+
+```js src/App.js active
+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') ?? ''
+ );
+
+ function handleChange(event) {
+ const nextDraft = event.target.value;
+ setDraft(nextDraft);
+ localStorage.setItem('draft', nextDraft);
+ }
+
+ return (
+
+ );
+}
+
+export default function App() {
+ return (
+ <>
+
Saved draft
+ Loading draft...
}>
+
+
+ >
+ );
+}
+```
+
+```js src/Document.js hidden
+import App from './App.js';
+
+export default function Document() {
+ return (
+
+
+ Saved draft
+
+
+
+
+
+
+ );
+}
+```
+
+```js src/index.js hidden
+import { hydrateRoot } from 'react-dom/client';
+import { renderToReadableStream } from 'react-dom/server';
+import Document from './Document.js';
+import { flushReadableStreamToFrame } from './demo-helpers.js';
+import './styles.css';
+
+async function main(frame) {
+ const stream = await renderToReadableStream();
+ await flushReadableStreamToFrame(stream, frame);
+
+ // Wait so both the fallback and hydrated content are visible.
+ await new Promise(resolve => setTimeout(resolve, 1200));
+ hydrateRoot(frame.contentDocument, );
+}
+
+main(document.getElementById('preview'));
+```
+
+```js src/demo-helpers.js hidden
+export async function flushReadableStreamToFrame(readable, frame) {
+ const doc = frame.contentWindow.document;
+ const decoder = new TextDecoder();
+ const reader = readable.getReader();
+
+ while (true) {
+ const {done, value} = await reader.read();
+ if (done) {
+ break;
+ }
+ doc.write(decoder.decode(value, {stream: true}));
+ }
+
+ doc.write(decoder.decode());
+ doc.close();
+}
+```
+
+```html public/index.html hidden
+
+
+
+
+ Browser-only rendering
+
+
+
+
+
+```
+
+```css src/styles.css hidden
+iframe {
+ width: 100%;
+ height: 160px;
+ border: 0;
+}
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
+ "react-scripts": "latest"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
+```
+
+
+
+
+
+In a React Server Components app, `use(browser())` must be called from a Client Component. If your framework uses Server Components by default, add the [`'use client'`](/reference/rsc/use-client) directive to that file or move the call to a child Client Component:
+
+```js {1}
+'use client';
+
+import { use, useState } from 'react';
+import { browser } from 'react-dom';
+
+export default function SavedDraft() {
+ use(browser('The saved draft is stored in localStorage.'));
+ const [draft] = useState(() => localStorage.getItem('draft') ?? '');
+ return ;
+}
+```
+
+
+
+---
+
+### Conditionally rendering on the server {/*conditionally-rendering-on-the-server*/}
+
+Like other calls to [`use`](/reference/react/use), `use(browser())` can be called inside a conditional statement or after an early return. This lets a Component or custom Hook opt out of server rendering based on a condition, such as the value of a prop.
+
+For example, this `useTimeZone` Hook accepts an optional default value. When provided, React renders the default value in the initial HTML and in the browser. Without a default value, the Component suspends during server rendering and shows the device's local time zone in the browser.
+
+Click **Reload** to see the loading fallback before the user's time zone appears.
+
+
+
+```js src/App.js
+import { Suspense } from 'react';
+import { useTimeZone } from './useTimeZone.js';
+
+function TimeZone({label, defaultTimeZone}) {
+ const timeZone = useTimeZone(defaultTimeZone);
+ return
+
+ Loading your time zone...}>
+
+
+ >
+ );
+}
+```
+
+```js src/useTimeZone.js active
+import { use } from 'react';
+import { browser } from 'react-dom';
+
+export function useTimeZone(defaultTimeZone) {
+ if (defaultTimeZone !== undefined) {
+ return defaultTimeZone;
+ }
+
+ use(browser('No default time zone was provided.'));
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
+}
+```
+
+```js src/Document.js hidden
+import App from './App.js';
+
+export default function Document() {
+ return (
+
+
+ Event details
+
+
+
+
+
+
+ );
+}
+```
+
+```js src/index.js hidden
+import { hydrateRoot } from 'react-dom/client';
+import { renderToReadableStream } from 'react-dom/server';
+import Document from './Document.js';
+import { flushReadableStreamToFrame } from './demo-helpers.js';
+import './styles.css';
+
+async function main(frame) {
+ const stream = await renderToReadableStream();
+ await flushReadableStreamToFrame(stream, frame);
+
+ // Wait so both the fallback and hydrated content are visible.
+ await new Promise(resolve => setTimeout(resolve, 1200));
+ hydrateRoot(frame.contentDocument, );
+}
+
+main(document.getElementById('preview'));
+```
+
+```js src/demo-helpers.js hidden
+export async function flushReadableStreamToFrame(readable, frame) {
+ const doc = frame.contentWindow.document;
+ const decoder = new TextDecoder();
+ const reader = readable.getReader();
+
+ while (true) {
+ const {done, value} = await reader.read();
+ if (done) {
+ break;
+ }
+ doc.write(decoder.decode(value, {stream: true}));
+ }
+
+ doc.write(decoder.decode());
+ doc.close();
+}
+```
+
+```html public/index.html hidden
+
+
+
+
+ Conditional browser rendering
+
+
+
+
+
+```
+
+```css src/styles.css hidden
+iframe {
+ width: 100%;
+ height: 240px;
+ border: 0;
+}
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "19.3.0-canary-eb8feb71-20260814",
+ "react-dom": "19.3.0-canary-eb8feb71-20260814",
+ "react-scripts": "latest"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
+```
+
+
+
+You can apply a similar pattern to conditionally avoid server rendering when using a Suspense-enabled data-fetching library:
+
+```js {3}
+function useBrowserQuery(query, options) {
+ if (options.initialData === undefined) {
+ use(browser('useBrowserQuery: No initial data was provided.'));
+ }
+
+ return useQuery(query, options);
+}
+
+function ProductDetails({ productId, initialData }) {
+ const product = useBrowserQuery(`/api/products/${productId}`, {
+ initialData,
+ });
+
+ return
{product.name}
;
+}
+```
+
+With `initialData`, React renders the Component to HTML on the server. Without it, React leaves the closest [``](/reference/react/Suspense) boundary's fallback in the HTML. In the browser, `useQuery` can fetch the data or read it from its client cache as usual.
+
+---
+
+### Reporting browser-only rendering on the server {/*reporting-browser-only-rendering-on-the-server*/}
+
+Pass an `onBrowserBailout` callback to the server renderer to report browser-only rendering. When React leaves a Suspense fallback for the browser, it does not call the server renderer's `onError` callback or [`hydrateRoot`'s `onRecoverableError`](/reference/react-dom/client/hydrateRoot#error-logging-in-production) callback. This example also passes a reason, which is available as the reported error's `cause`:
+
+```js
+import { Suspense, use, useState } from 'react';
+import { browser } from 'react-dom';
+import { renderToPipeableStream } from 'react-dom/server';
+
+function SavedDraft() {
+ use(browser(() => new Error('The saved draft is stored in localStorage.')));
+ const [draft] = useState(() => localStorage.getItem('draft') ?? '');
+ return ;
+}
+
+function App() {
+ return (
+ Loading saved draft...}>
+
+
+ );
+}
+
+const { pipe } = renderToPipeableStream(, {
+ onShellReady() {
+ pipe(response);
+ },
+ onBrowserBailout(error, errorInfo) {
+ logBrowserBailout(error, errorInfo);
+ }
+});
+```
+
+`onBrowserBailout` receives two arguments:
+
+1. An `Error` describing the browser-only render. If you passed a reason to `browser`, it is available as the error's `cause`.
+2. An `errorInfo` object with a `componentStack` showing where browser-only rendering occurred.
+
+The reason function can return any value. Return a new `Error` to give the cause its own stack without creating the `Error` in the browser. React does not serialize the reason into the HTML.
+
+If there is no Suspense boundary to provide a fallback, the server render fails. React reports the failure through the renderer's usual error callbacks instead of `onBrowserBailout`.
+
+---
+
+### Aborting pending server rendering for the browser {/*aborting-pending-server-rendering-for-the-browser*/}
+
+If you call a server rendering API directly, you can stop waiting for pending content and let the browser finish rendering it. Pass the value returned by `browser` as the reason when aborting the server render. React then leaves pending Suspense boundaries in their fallback state and renders their content in the browser:
+
+```js {1,8}
+import { browser } from 'react-dom';
+import { renderToPipeableStream } from 'react-dom/server';
+
+const { pipe, abort } = renderToPipeableStream(, {
+ onShellReady() {
+ pipe(response);
+ setTimeout(() => {
+ abort(browser('The server render timed out.'));
+ }, 10000);
+ }
+});
+```
+
+A `browser` abort reason does not trigger the server renderer's `onError` callback or `hydrateRoot`'s `onRecoverableError` callback. Instead, the server renderer reports each recovered Suspense boundary to `onBrowserBailout`.
+
+For server rendering APIs that accept an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal), pass `browser()` as the reason to [`AbortController.abort`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort).
diff --git a/src/content/reference/react-dom/client/hydrateRoot.md b/src/content/reference/react-dom/client/hydrateRoot.md
index e1a9c65e2..a606e5c87 100644
--- a/src/content/reference/react-dom/client/hydrateRoot.md
+++ b/src/content/reference/react-dom/client/hydrateRoot.md
@@ -45,6 +45,7 @@ React will attach to the HTML that exists inside the `domNode`, and take over ma
* **optional** `onUncaughtError`: Callback called when an error is thrown and not caught by an Error Boundary. Called with the `error` that was thrown and an `errorInfo` object containing the `componentStack`.
* **optional** `onRecoverableError`: Callback called when React automatically recovers from errors. Called with the `error` React throws, and an `errorInfo` object containing the `componentStack`. Some recoverable errors may include the original error cause as `error.cause`.
* **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as used on the server.
+ * **optional** `formState`: The form state from a form submission handled by a [Server Function](/reference/rsc/server-functions). If the page was rendered on the server in response to a submission of a form that uses [`useActionState`](/reference/react/useActionState) with a `permalink`, pass the resulting form state so that `useActionState` returns the submitted state instead of the `initialState`. Must be the same value as the `formState` passed to the [server renderer.](/reference/react-dom/server/renderToPipeableStream#parameters) This is typically passed through by your framework.
#### Returns {/*returns*/}
@@ -274,6 +275,8 @@ This only works one level deep, and is intended to be an escape hatch. Don’t o
---
+{/* TODO: Remove this subsection when browser is available in Stable. */}
+
### Handling different client and server content {/*handling-different-client-and-server-content*/}
If you intentionally need to render something different on the server and the client, you can do a two-pass rendering. Components that render something different on the client can read a [state variable](/reference/react/useState) like `isClient`, which you can set to `true` in an [Effect](/reference/react/useEffect):
@@ -319,6 +322,10 @@ export default function App() {
This way the initial render pass will render the same content as the server, avoiding mismatches, but an additional pass will happen synchronously right after hydration.
+Use this approach when you want the client-rendered content to be different from the initial server-rendered HTML.
+
+If a component should render only in the browser, call [`use(browser())`](/reference/react/use#use-browser) instead of waiting for an Effect.
+
This approach makes hydration slower because your components have to render twice. Be mindful of the user experience on slow connections. The JavaScript code may load significantly later than the initial HTML render, so rendering a different UI immediately after hydration may also feel jarring to the user.
diff --git a/src/content/reference/react-dom/index.md b/src/content/reference/react-dom/index.md
index 76e56cd15..1f14ed15d 100644
--- a/src/content/reference/react-dom/index.md
+++ b/src/content/reference/react-dom/index.md
@@ -30,6 +30,12 @@ Bu API'ler, kaynakların (örneğin, betikler, stil sayfaları ve fontlar) önce
* [`preinit`](/reference/react-dom/preinit), harici bir betiği önceden yükleyip değerlendirmenize veya bir stil sayfasını yükleyip yerleştirmenize olanak tanır.
* [`preinitModule`](/reference/react-dom/preinitModule), bir ESM modülünü önceden yükleyip değerlendirmenizi sağlar.
+## Server Rendering APIs {/*server-rendering-apis*/}
+
+This API controls how components render on the server:
+
+* [`browser`](/reference/react-dom/browser) lets you mark a component as browser-only during server rendering.
+
---
## Giriş noktaları {/*entry-points*/}
diff --git a/src/content/reference/react-dom/server/index.md b/src/content/reference/react-dom/server/index.md
index 8005f0f02..da6770fde 100644
--- a/src/content/reference/react-dom/server/index.md
+++ b/src/content/reference/react-dom/server/index.md
@@ -14,8 +14,13 @@ title: Sunucu React DOM API'leri
Bu metodlar sadece tarayıcılar, Deno ve bazı modern edge runtime'ları gibi [Web Stream'leri](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) içeren ortamlarda kullanılabilir:
+<<<<<<< HEAD
* [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream), bir React ağacını [Readable Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) olarak render eder.
* [`resume`](/reference/react-dom/server/renderToPipeableStream), [`prerender`](/reference/react-dom/static/prerender) işlemini bir [Readable Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) olarak devam ettirir.
+=======
+* [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) renders a React tree to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
+* [`resume`](/reference/react-dom/server/resume) resumes [`prerender`](/reference/react-dom/static/prerender) to a [Readable Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
+>>>>>>> f3d9794fc31f4a3faf7e863984d37f4ae86b3290
@@ -29,8 +34,13 @@ Node.js ayrıca uyumluluk için bu yöntemleri de içerir, ancak performansın d
Bu yöntemler yalnızca [Node.js Stream'leri](https://nodejs.org/api/stream.html) olan ortamlarda kullanılabilir:
+<<<<<<< HEAD
* [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream), bir React ağacını pipe edilebilir bir [Node.js Stream](https://nodejs.org/api/stream.html) olarak render eder.
* [`resumeToPipeableStream`](/reference/react-dom/server/renderToPipeableStream), [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) işlemini pipe edilebilir bir [Node.js Stream](https://nodejs.org/api/stream.html) olarak devam ettirir.
+=======
+* [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) renders a React tree to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html)
+* [`resumeToPipeableStream`](/reference/react-dom/server/resumeToPipeableStream) resumes [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html)
+>>>>>>> f3d9794fc31f4a3faf7e863984d37f4ae86b3290
---
diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md
index ea26a99b8..0ec475acb 100644
--- a/src/content/reference/react-dom/server/renderToPipeableStream.md
+++ b/src/content/reference/react-dom/server/renderToPipeableStream.md
@@ -48,6 +48,7 @@ const { pipe } = renderToPipeableStream(, {
* `reactNode`: HTML'e render etmek istediğiniz React düğümüdür. Örneğin, `` gibi bir JSX bileşeni. Belgenin tamamını temsil etmesi beklediğinden dolayı `App` bileşeni `` etiketini render etmelidir.
+<<<<<<< HEAD
* **optional** `options`: Streaming options içeren bir object.
* **optional** `bootstrapScriptContent`: Belirtilirse, bu string inline bir `