Skip to content

Commit c0a3d24

Browse files
Auto-merged main into codex/ios-safari-header-flicker-tests on deployment.
2 parents 92bdeff + 5b3692a commit c0a3d24

22 files changed

Lines changed: 652 additions & 22 deletions

src/content/community/conferences.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,41 @@ Do you know of a local React.js conference? Add it here! (Please keep the list c
1010

1111
## Upcoming Conferences {/*upcoming-conferences*/}
1212

13-
### React Paris 2026 {/*react-paris-2026*/}
14-
March 26 - 27, 2026. In-person in Paris, France (hybrid event)
13+
### ZurichJS Conf 2026 {/*zurichjs-conf-2026*/}
14+
September 10-11, 2026. In-person in Zurich, Switzerland
1515

16-
[Website](https://react.paris/) - [Twitter](https://x.com/BeJS_)
16+
[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/)
1717

18-
### CityJS London 2026 {/*cityjs-london-2026*/}
19-
April 14-17, 2026. In-person in London
18+
### React Alicante 2026 {/*react-alicante-2026*/}
19+
Sep 24 - 26, 2026. In-person in Alicante
2020

21-
[Website](https://india.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)
21+
[Website](https://reactalicante.es//) - [Twitter](https://x.com/ReactAlicante)
2222

23-
### ZurichJS Conf 2026 {/*zurichjs-conf-2026*/}
24-
September 10-11, 2026. In-person in Zurich, Switzerland
23+
### CityJS Athens 2026 {/*cityjs-athens-2026*/}
24+
October 21-23, 2026. In-person in Athens
25+
26+
[Website](https://athens.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)
27+
28+
29+
## Past Conferences {/*past-conferences*/}
2530

26-
[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/)
2731

2832
### React Conf Japan 2027 {/*react-conf-japan-2027*/}
2933
April 24, 2027. In-person in Tokyo, Japan
3034

3135
[Website](https://reactconf.jp/) - [Twitter](https://x.com/reactconfjp)
3236

33-
## Past Conferences {/*past-conferences*/}
37+
### CityJS London 2026 {/*cityjs-london-2026*/}
38+
April 14-17, 2026. In-person in London
39+
40+
[Website](https://india.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)
41+
42+
43+
### React Paris 2026 {/*react-paris-2026*/}
44+
March 26 - 27, 2026. In-person in Paris, France (hybrid event)
45+
46+
[Website](https://react.paris/) - [Twitter](https://x.com/BeJS_)
47+
3448

3549
### CityJS New Delhi 2026 {/*cityjs-newdelhi-2026*/}
3650
February 12-13, 2026. In-person in New Delhi, India

src/content/community/team.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ React work is organized into working groups, each responsible for an area of the
2626

2727
<TeamMember name="Moti Zilberman" permalink="moti-zilberman" photo="/images/team/gh-motiz88.jpg" github="motiz88" title="Engineer at Meta" group="DevX*" />
2828

29-
<TeamMember name="Nicola Corti" permalink="nicola-corti" photo="/images/team/gh-cortinico.jpg" github="cortinico" title="Engineer at Meta" group="React Native*, Docs & Community" />
29+
<TeamMember name="Nicola Corti" permalink="nicola-corti" photo="/images/team/gh-cortinico.jpg" github="cortinico" twitter="cortinico" title="Engineer at Meta" group="React Native*, Docs & Community" />
3030

3131
<TeamMember name="Rick Hanlon" permalink="rick-hanlon" photo="/images/team/rickhanlonii.jpg" github="rickhanlonii" twitter="rickyfm" threads="rickhanlonii" bsky="ricky.fm" title="Engineer at Meta" group="Docs & Community*, DOM, Fiber, Server, DevX">
3232
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,7 +38,7 @@ React work is organized into working groups, each responsible for an area of the
3838

3939
## Working Group members {/*working-group-members*/}
4040

41-
<TeamMember name="Alex Hunt" permalink="alex-hunt" photo="/images/team/gh-huntie.jpg" github="huntie" title="Engineer at Meta" group="DevX, React Native" />
41+
<TeamMember name="Alex Hunt" permalink="alex-hunt" photo="/images/team/gh-huntie.jpg" github="huntie" twitter="huntie" title="Engineer at Meta" group="DevX, React Native" />
4242

4343
<TeamMember name="Aurora Scharff" permalink="aurora-scharff" photo="/images/team/gh-aurorascharff.jpg" github="aurorascharff" title="Engineer at Vercel" group="Docs & Community" />
4444

@@ -82,11 +82,11 @@ React work is organized into working groups, each responsible for an area of the
8282

8383
<TeamMember name="Pieter De Baets" permalink="pieter-de-baets" photo="/images/team/gh-javache.jpg" github="javache" title="Engineer at Meta" group="Compiler, Fiber, React Native" />
8484

85-
<TeamMember name="Riccardo Cipolleschi" permalink="riccardo-cipolleschi" photo="/images/team/gh-cipolleschi.png" github="cipolleschi" title="Engineer at Meta" group="Docs & Community, React Native" />
85+
<TeamMember name="Riccardo Cipolleschi" permalink="riccardo-cipolleschi" photo="/images/team/gh-cipolleschi.png" github="cipolleschi" twitter="CipolleschiR" title="Engineer at Meta" group="Docs & Community, React Native" />
8686

87-
<TeamMember name="Rob Hogan" permalink="rob-hogan" photo="/images/team/gh-rh389.jpg" github="rh389" title="Engineer at Meta" group="DevX" />
87+
<TeamMember name="Rob Hogan" permalink="rob-hogan" photo="/images/team/gh-robhogan.jpg" github="robhogan" twitter="robjhogan" title="Engineer at Meta" group="DevX" />
8888

89-
<TeamMember name="Rubén Norte" permalink="ruben-norte" photo="/images/team/gh-rubennorte.jpg" github="rubennorte" title="Engineer at Meta" group="Fiber" />
89+
<TeamMember name="Rubén Norte" permalink="ruben-norte" photo="/images/team/gh-rubennorte.jpg" github="rubennorte" title="Engineer at Meta" group="Fiber, React Native" />
9090

9191
<TeamMember name="Ruslan Lesiutin" permalink="ruslan-lesiutin" photo="/images/team/lesiutin.jpg" github="hoxyq" twitter="ruslanlesiutin" threads="lesiutin" title="Engineer at Meta" group="DOM, DevX">
9292
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.
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
---
2+
title: browser
3+
version: canary
4+
---
5+
6+
<Intro>
7+
8+
<Canary>
9+
10+
**The `browser` API is currently only available in React’s Canary and Experimental channels.**
11+
12+
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
13+
14+
</Canary>
15+
16+
`browser` lets you mark a component as browser-only during server rendering.
17+
18+
```js
19+
use(browser(reason?))
20+
```
21+
22+
</Intro>
23+
24+
<InlineToc />
25+
26+
---
27+
28+
## Reference {/*reference*/}
29+
30+
### `browser(reason?)` {/*browser*/}
31+
32+
Call `browser` inside [`use`](/reference/react/use) to mark a component as browser-only during server rendering:
33+
34+
```js
35+
import { use } from 'react';
36+
import { browser } from 'react-dom';
37+
38+
function BrowserOnly() {
39+
use(browser('This component requires browser APIs.'));
40+
return <BrowserContent />;
41+
}
42+
```
43+
44+
During server rendering, `use(browser())` stops rendering the component and leaves the closest [`<Suspense>`](/reference/react/Suspense) boundary's fallback in its place. In the browser, `use(browser())` returns `undefined`, so the component renders normally.
45+
46+
[See more examples below.](#usage)
47+
48+
#### Parameters {/*parameters*/}
49+
50+
* **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(...)`.
51+
52+
#### Returns {/*returns*/}
53+
54+
`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`.
55+
56+
#### Caveats {/*caveats*/}
57+
58+
* `use(browser())` must be inside a `<Suspense>` boundary during server rendering. Without one, the server render fails.
59+
* 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).
60+
* 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.
61+
62+
---
63+
64+
## Usage {/*usage*/}
65+
66+
### Rendering content only in the browser {/*rendering-content-only-in-the-browser*/}
67+
68+
Call `browser` inside `use` in a component that should only render in the browser:
69+
70+
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.
71+
72+
Press **Render the page**. The loading fallback appears first. After a short delay, React hydrates the page and displays the browser-only editor.
73+
74+
<Sandpack>
75+
76+
```js src/App.js active
77+
import { Suspense, use } from 'react';
78+
import { browser } from 'react-dom';
79+
80+
function BrowserOnlyEditor() {
81+
use(browser('The editor requires browser APIs.'));
82+
return <label>Draft: <input /></label>;
83+
}
84+
85+
export default function App() {
86+
return (
87+
<Suspense fallback={<p>Loading editor...</p>}>
88+
<BrowserOnlyEditor />
89+
</Suspense>
90+
);
91+
}
92+
```
93+
94+
```js src/Document.js hidden
95+
import App from './App.js';
96+
97+
export default function Document() {
98+
return (
99+
<html lang="en">
100+
<head>
101+
<title>Article editor</title>
102+
</head>
103+
<body>
104+
<h1>Article editor</h1>
105+
<App />
106+
</body>
107+
</html>
108+
);
109+
}
110+
```
111+
112+
```js src/index.js
113+
import { hydrateRoot } from 'react-dom/client';
114+
import { renderToReadableStream } from 'react-dom/server';
115+
import Document from './Document.js';
116+
import { flushReadableStreamToFrame } from './demo-helpers.js';
117+
import './styles.css';
118+
119+
async function main(frame) {
120+
const stream = await renderToReadableStream(<Document />);
121+
await flushReadableStreamToFrame(stream, frame);
122+
123+
// Wait so both the fallback and hydrated content are visible.
124+
await new Promise(resolve => setTimeout(resolve, 1200));
125+
hydrateRoot(frame.contentDocument, <Document />);
126+
}
127+
128+
const renderButton = document.getElementById('render');
129+
renderButton.addEventListener('click', () => {
130+
renderButton.disabled = true;
131+
main(document.getElementById('preview'));
132+
}, { once: true });
133+
```
134+
135+
```js src/demo-helpers.js hidden
136+
export async function flushReadableStreamToFrame(readable, frame) {
137+
const doc = frame.contentWindow.document;
138+
const decoder = new TextDecoder();
139+
for await (const chunk of readable) {
140+
doc.write(decoder.decode(chunk, { stream: true }));
141+
}
142+
doc.close();
143+
}
144+
```
145+
146+
```html public/index.html
147+
<!DOCTYPE html>
148+
<html lang="en">
149+
<head>
150+
<meta charset="UTF-8" />
151+
<title>Browser-only rendering</title>
152+
</head>
153+
<body>
154+
<button id="render">Render the page</button>
155+
<br /><br />
156+
<iframe id="preview" title="Rendered page"></iframe>
157+
</body>
158+
</html>
159+
```
160+
161+
```css src/styles.css hidden
162+
iframe {
163+
width: 100%;
164+
height: 180px;
165+
border: 1px solid #aaa;
166+
}
167+
```
168+
169+
```json package.json hidden
170+
{
171+
"dependencies": {
172+
"react": "canary",
173+
"react-dom": "canary",
174+
"react-scripts": "latest"
175+
},
176+
"scripts": {
177+
"start": "react-scripts start",
178+
"build": "react-scripts build",
179+
"test": "react-scripts test --env=jsdom",
180+
"eject": "react-scripts eject"
181+
}
182+
}
183+
```
184+
185+
</Sandpack>
186+
187+
<Note>
188+
189+
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:
190+
191+
```js {1}
192+
'use client';
193+
194+
import { use } from 'react';
195+
import { browser } from 'react-dom';
196+
197+
export default function BrowserOnlyEditor() {
198+
use(browser('The editor requires browser APIs.'));
199+
return <Editor />;
200+
}
201+
```
202+
203+
</Note>
204+
205+
---
206+
207+
### Conditionally rendering in the browser {/*conditionally-rendering-in-the-browser*/}
208+
209+
Like other calls to [`use`](/reference/react/use), you can call `use(browser())` conditionally or inside a custom Hook. For example, you can wrap a Suspense-enabled data-fetching library's `useQuery` and skip server rendering when initial data is missing:
210+
211+
```js {3}
212+
function useBrowserQuery(query, options) {
213+
if (options.initialData === undefined) {
214+
use(browser('useBrowserQuery: No initial data was provided.'));
215+
}
216+
217+
return useQuery(query, options);
218+
}
219+
220+
function ProductDetails({ productId, initialData }) {
221+
const product = useBrowserQuery(`/api/products/${productId}`, {
222+
initialData,
223+
});
224+
225+
return <h1>{product.name}</h1>;
226+
}
227+
```
228+
229+
On the server, `useBrowserQuery` calls `useQuery` only when `initialData` is available. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the query library can fetch the data or read it from its client cache.
230+
231+
---
232+
233+
### Reporting browser-only rendering on the server {/*reporting-browser-only-rendering-on-the-server*/}
234+
235+
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`:
236+
237+
```js
238+
import { Suspense, use } from 'react';
239+
import { browser } from 'react-dom';
240+
import { renderToPipeableStream } from 'react-dom/server';
241+
242+
function BrowserOnlyEditor() {
243+
use(browser(() => new Error('The editor requires a browser API.')));
244+
return <Editor />;
245+
}
246+
247+
const { pipe } = renderToPipeableStream(
248+
<Suspense fallback={<p>Loading editor...</p>}>
249+
<BrowserOnlyEditor />
250+
</Suspense>,
251+
{
252+
onShellReady() {
253+
pipe(response);
254+
},
255+
onBrowserBailout(error, errorInfo) {
256+
logBrowserBailout(error, errorInfo);
257+
}
258+
}
259+
);
260+
```
261+
262+
`onBrowserBailout` receives two arguments:
263+
264+
1. An `Error` describing the browser-only render. If you passed a reason to `browser`, it is available as the error's `cause`.
265+
2. An `errorInfo` object with a `componentStack` showing where browser-only rendering occurred.
266+
267+
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.
268+
269+
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`.
270+
271+
---
272+
273+
### Aborting pending server rendering for the browser {/*aborting-pending-server-rendering-for-the-browser*/}
274+
275+
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:
276+
277+
```js {1,8}
278+
import { browser } from 'react-dom';
279+
import { renderToPipeableStream } from 'react-dom/server';
280+
281+
const { pipe, abort } = renderToPipeableStream(<App />, {
282+
onShellReady() {
283+
pipe(response);
284+
setTimeout(() => {
285+
abort(browser('The server render timed out.'));
286+
}, 10000);
287+
}
288+
});
289+
```
290+
291+
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`.
292+
293+
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).

src/content/reference/react-dom/client/hydrateRoot.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ This only works one level deep, and is intended to be an escape hatch. Don’t o
274274
275275
---
276276
277+
{/* TODO: Remove this subsection when browser is available in Stable. */}
278+
277279
### Handling different client and server content {/*handling-different-client-and-server-content*/}
278280
279281
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 +321,10 @@ export default function App() {
319321
320322
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.
321323
324+
Use this approach when you want the client-rendered content to be different from the initial server-rendered HTML.
325+
326+
<Canary>If a component should render only in the browser, call [`use(browser())`](/reference/react/use#use-browser) instead of waiting for an Effect.</Canary>
327+
322328
<Pitfall>
323329
324330
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.

0 commit comments

Comments
 (0)