From 0a74f01ee04370fb3a1be2552866c030b90a885a Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Fri, 22 Aug 2025 09:13:00 -0700 Subject: [PATCH 001/196] Add more information about event and property binding on custom elements (#7939) Co-authored-by: Sebastian "Sebbie" Silbermann --- .../reference/react-dom/components/index.md | 134 ++++++++++++++++-- 1 file changed, 124 insertions(+), 10 deletions(-) diff --git a/src/content/reference/react-dom/components/index.md b/src/content/reference/react-dom/components/index.md index ec2e1d2ee..586663398 100644 --- a/src/content/reference/react-dom/components/index.md +++ b/src/content/reference/react-dom/components/index.md @@ -162,23 +162,137 @@ Similar to the [DOM standard,](https://developer.mozilla.org/en-US/docs/Web/API/ ### Custom HTML elements {/*custom-html-elements*/} -If you render a tag with a dash, like ``, React will assume you want to render a [custom HTML element.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) In React, rendering custom elements works differently from rendering built-in browser tags: - -- All custom element props are serialized to strings and are always set using attributes. -- Custom elements accept `class` rather than `className`, and `for` rather than `htmlFor`. +If you render a tag with a dash, like ``, React will assume you want to render a [custom HTML element.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) If you render a built-in browser HTML element with an [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is) attribute, it will also be treated as a custom element. - +#### Setting values on custom elements {/*attributes-vs-properties*/} + +Custom elements have two methods of passing data into them: + +1) Attributes: Which are displayed in markup and can only be set to string values +2) Properties: Which are not displayed in markup and can be set to arbitrary JavaScript values + +By default, React will pass values bound in JSX as attributes: + +```jsx + +``` + +Non-string JavaScript values passed to custom elements will be serialized by default: + +```jsx +// Will be passed as `"1,2,3"` as the output of `[1,2,3].toString()` + +``` + +React will, however, recognize an custom element's property as one that it may pass arbitrary values to if the property name shows up on the class during construction: + + + +```js src/index.js hidden +import {MyElement} from './MyElement.js'; +import { createRoot } from 'react-dom/client'; +import {App} from "./App.js"; + +customElements.define('my-element', MyElement); + +const root = createRoot(document.getElementById('root')) +root.render(); +``` + +```js src/MyElement.js active +export class MyElement extends HTMLElement { + constructor() { + super(); + // The value here will be overwritten by React + // when initialized as an element + this.value = undefined; + } + + connectedCallback() { + this.innerHTML = this.value.join(", "); + } +} +``` -[A future version of React will include more comprehensive support for custom elements.](https://github.com/facebook/react/issues/11347#issuecomment-1122275286) +```js src/App.js +export function App() { + return +} +``` -You can try it by upgrading React packages to the most recent experimental version: + + +#### Listening for events on custom elements {/*custom-element-events*/} + +A common pattern when using custom elements is that they may dispatch [`CustomEvent`s](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) rather than accept a function to call when an event occur. You can listen for these events using an `on` prefix when binding to the event via JSX. + + + +```js src/index.js hidden +import {MyElement} from './MyElement.js'; +import { createRoot } from 'react-dom/client'; +import {App} from "./App.js"; + +customElements.define('my-element', MyElement); + +const root = createRoot(document.getElementById('root')) +root.render(); +``` + +```javascript src/MyElement.js +export class MyElement extends HTMLElement { + constructor() { + super(); + this.test = undefined; + this.emitEvent = this._emitEvent.bind(this); + } + + _emitEvent() { + const event = new CustomEvent('speak', { + detail: { + message: 'Hello, world!', + }, + }); + this.dispatchEvent(event); + } + + connectedCallback() { + this.el = document.createElement('button'); + this.el.innerText = 'Say hi'; + this.el.addEventListener('click', this.emitEvent); + this.appendChild(this.el); + } + + disconnectedCallback() { + this.el.removeEventListener('click', this.emitEvent); + } +} +``` + +```jsx src/App.js active +export function App() { + return ( + console.log(e.detail.message)} + > + ) +} +``` + + + + -- `react@experimental` -- `react-dom@experimental` +Events are case-sensitive and support dashes (`-`). Preserve the casing of the event and include all dashes when listening for custom element's events: -Experimental versions of React may contain bugs. Don't use them in production. +```jsx +// Listens for `say-hi` events + +// Listens for `sayHi` events + +``` --- From 27d86ffe6ec82e3642c6490d2187bae2271020a4 Mon Sep 17 00:00:00 2001 From: Sam Selikoff Date: Fri, 22 Aug 2025 18:04:04 -0400 Subject: [PATCH 002/196] Touch-ups to Activity (#7940) --- src/content/reference/react/Activity.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/content/reference/react/Activity.md b/src/content/reference/react/Activity.md index f437a74da..09b79c79f 100644 --- a/src/content/reference/react/Activity.md +++ b/src/content/reference/react/Activity.md @@ -51,7 +51,7 @@ While hidden, children still re-render in response to new props, albeit at a low When the boundary becomes visible again, React will reveal the children with their previous state restored, and re-create their Effects. -In this way, Activity can thought of as a mechanism for rendering "background activity". Rather than completely discarding content that's likely to become visible again, you can use Activity to maintain and restore that content's UI and internal state, while ensuring hidden content has no unwanted side effects. +In this way, Activity can be thought of as a mechanism for rendering "background activity". Rather than completely discarding content that's likely to become visible again, you can use Activity to maintain and restore that content's UI and internal state, while ensuring that your hidden content has no unwanted side effects. [See more examples below.](#usage) @@ -62,7 +62,7 @@ In this way, Activity can thought of as a mechanism for rendering "background ac #### Caveats {/*caveats*/} -- When used with ``, hidden activities that reveal in a transition will activate an "enter" animation. Visible Activities hidden in a transition will activate an "exit" animation. +- If an Activity is rendered inside of a [ViewTransition](/reference/react/ViewTransition), and it becomes visible as a result of an update caused by [startTransition](/reference/react/startTransition), it will activate the ViewTransition's `enter` animation. If it becomes hidden, it will activate its `exit` animation. --- @@ -70,7 +70,7 @@ In this way, Activity can thought of as a mechanism for rendering "background ac ### Restoring the state of hidden components {/*restoring-the-state-of-hidden-components*/} -Typically in React, when you want to conditionally show or hide a component, you mount and unmount it: +In React, when you want to conditionally show or hide a component, you typically mount or unmount it based on that condition: ```jsx {isShowingSidebar && ( @@ -88,11 +88,11 @@ When you hide a component using an Activity boundary instead, React will "save" ``` -This makes it possible to restore components to their previous state. +This makes it possible to hide and then later restore components in the state they were previously in. -The following example has a sidebar with an expandable section – you can press "Overview" to reveal the three subitems below it. The main app area also has a button that hides and shows the sidebar. +The following example has a sidebar with an expandable section. You can press "Overview" to reveal the three subitems below it. The main app area also has a button that hides and shows the sidebar. -Try expanding the Overview section, then toggling the sidebar closed and open: +Try expanding the Overview section, and then toggling the sidebar closed then open: From 694aeac10e5e44a5ee34cdbe035857fa4cf6b2fc Mon Sep 17 00:00:00 2001 From: Ben Amor Aymen Date: Tue, 26 Aug 2025 15:57:39 +0200 Subject: [PATCH 003/196] Add React Paris 2025 conference talks + Add React Paris 2026 (#7935) Co-authored-by: Aimen Ben Amor --- src/content/community/conferences.md | 68 +++++++++++++++------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index a97f3d5b4..b811b983c 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -10,36 +10,6 @@ Do you know of a local React.js conference? Add it here! (Please keep the list c ## Upcoming Conferences {/*upcoming-conferences*/} -### CityJS London 2025 {/*cityjs-london*/} -April 23 - 25, 2025. In-person in London, UK - -[Website](https://london.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social) - -### App.js Conf 2025 {/*appjs-conf-2025*/} -May 28 - 30, 2025. In-person in Kraków, Poland + remote - -[Website](https://appjs.co) - [Twitter](https://twitter.com/appjsconf) - -### CityJS Athens 2025 {/*cityjs-athens*/} -May 27 - 31, 2025. In-person in Athens, Greece - -[Website](https://athens.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social) - -### React Norway 2025 {/*react-norway-2025*/} -June 13, 2025. In-person in Oslo, Norway + remote (virtual event) - -[Website](https://reactnorway.com/) - [Twitter](https://x.com/ReactNorway) - -### React Summit 2025 {/*react-summit-2025*/} -June 13 - 17, 2025. In-person in Amsterdam, Netherlands + remote (hybrid event) - -[Website](https://reactsummit.com/) - [Twitter](https://x.com/reactsummit) - -### React Nexus 2025 {/*react-nexus-2025*/} -July 03 - 05, 2025. In-person in Bangalore, India - -[Website](https://reactnexus.com/) - [Twitter](https://x.com/ReactNexus) - [Bluesky](https://bsky.app/profile/reactnexus.com) - [Linkedin](https://www.linkedin.com/company/react-nexus) - [YouTube](https://www.youtube.com/reactify_in) - ### React Universe Conf 2025 {/*react-universe-conf-2025*/} September 2-4, 2025. Wrocław, Poland. @@ -70,13 +40,49 @@ November 28 & December 1, 2025. In-person in London, UK + online (hybrid event) [Website](https://reactadvanced.com/) - [Twitter](https://x.com/reactadvanced) +### 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_) + ## Past Conferences {/*past-conferences*/} + +### React Nexus 2025 {/*react-nexus-2025*/} +July 03 - 05, 2025. In-person in Bangalore, India + +[Website](https://reactnexus.com/) - [Twitter](https://x.com/ReactNexus) - [Bluesky](https://bsky.app/profile/reactnexus.com) - [Linkedin](https://www.linkedin.com/company/react-nexus) - [YouTube](https://www.youtube.com/reactify_in) + +### React Summit 2025 {/*react-summit-2025*/} +June 13 - 17, 2025. In-person in Amsterdam, Netherlands + remote (hybrid event) + +[Website](https://reactsummit.com/) - [Twitter](https://x.com/reactsummit) + +### React Norway 2025 {/*react-norway-2025*/} +June 13, 2025. In-person in Oslo, Norway + remote (virtual event) + +[Website](https://reactnorway.com/) - [Twitter](https://x.com/ReactNorway) + +### CityJS Athens 2025 {/*cityjs-athens*/} +May 27 - 31, 2025. In-person in Athens, Greece + +[Website](https://athens.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social) + +### App.js Conf 2025 {/*appjs-conf-2025*/} +May 28 - 30, 2025. In-person in Kraków, Poland + remote + +[Website](https://appjs.co) - [Twitter](https://twitter.com/appjsconf) + +### CityJS London 2025 {/*cityjs-london*/} +April 23 - 25, 2025. In-person in London, UK + +[Website](https://london.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social) + ### React Paris 2025 {/*react-paris-2025*/} March 20 - 21, 2025. In-person in Paris, France (hybrid event) -[Website](https://react.paris/) - [Twitter](https://x.com/BeJS_) +[Website](https://react.paris/) - [Twitter](https://x.com/BeJS_) - [YouTube](https://www.youtube.com/playlist?list=PL53Z0yyYnpWitP8Zv01TSEQmKLvuRh_Dj) ### React Native Connection 2025 {/*react-native-connection-2025*/} April 3 (Reanimated Training) + April 4 (Conference), 2025. Paris, France. From 90686d80ab815a4d9c6b54794e41b2b4355402d0 Mon Sep 17 00:00:00 2001 From: Aris Markogiannakis Date: Tue, 26 Aug 2025 15:15:07 +0100 Subject: [PATCH 004/196] Add CityJS New Delhi 2026 conference details (#7949) * Add CityJS New Delhi 2026 conference details * Update CityJS New Delhi date to 2025 --- src/content/community/conferences.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index b811b983c..581046a8c 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -30,6 +30,12 @@ October 31 - November 01, 2025. In-person in Goa, India (hybrid event) + Oct 15 [Website](https://www.reactindia.io) - [Twitter](https://twitter.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia) - [Youtube](https://www.youtube.com/channel/UCaFbHCBkPvVv1bWs_jwYt3w) + +### CityJS New Delhi 2025 {/*cityjs-newdelhi*/} +November 6-7, 2025. In-person in New Delhi, India + +[Website](https://india.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social) + ### React Summit US 2025 {/*react-summit-us-2025*/} November 18 - 21, 2025. In-person in New York, USA + remote (hybrid event) From 9a370f2cf4598d51511db2942fcf8d6e7f1d3007 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 28 Aug 2025 16:08:37 -0400 Subject: [PATCH 005/196] [compiler] Tweak intro section on manual memo guidance (#7953) The previous sentence "If you are using React Compiler, useMemo, useCallback, and React.memo can be removed." was coming off a bit too strong and makes it incorrectly seem like the manual memos and compiler memos are 1:1. Removing the sentence doesn't take anything away from this paragraph, so let's remove it to reduce confusion. --- src/content/learn/react-compiler/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/learn/react-compiler/introduction.md b/src/content/learn/react-compiler/introduction.md index 440c66ab6..96fdf70db 100644 --- a/src/content/learn/react-compiler/introduction.md +++ b/src/content/learn/react-compiler/introduction.md @@ -154,7 +154,7 @@ Next.js users can enable the swc-invoked React Compiler by using [v15.3.1](https ## What should I do about useMemo, useCallback, and React.memo? {/*what-should-i-do-about-usememo-usecallback-and-reactmemo*/} -If you are using React Compiler, [`useMemo`](/reference/react/useMemo), [`useCallback`](/reference/react/useCallback), and [`React.memo`](/reference/react/memo) can be removed. React Compiler adds automatic memoization more precisely and granularly than is possible with these hooks. If you choose to keep manual memoization, React Compiler will analyze them and determine if your manual memoization matches its automatically inferred memoization. If there isn't a match, the compiler will choose to bail out of optimizing that component. +React Compiler adds automatic memoization more precisely and granularly than is possible with [`useMemo`](/reference/react/useMemo), [`useCallback`](/reference/react/useCallback), and [`React.memo`](/reference/react/memo). If you choose to keep manual memoization, React Compiler will analyze them and determine if your manual memoization matches its automatically inferred memoization. If there isn't a match, the compiler will choose to bail out of optimizing that component. This is done out of caution as a common anti-pattern with manual memoization is using it for correctness. This means your app depends on specific values being memoized to work properly. For example, in order to prevent an infinite loop, you may have memoized some values to stop a `useEffect` call from firing. This breaks the Rules of React, but since it can potentially be dangerous for the compiler to automatically remove manual memoization, the compiler will just bail out instead. You should manually remove your handwritten memoization and verify that your app still works as expected. From 94a116476c58897cd0d4745ef8bab6b2621ca38d Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 28 Aug 2025 18:06:22 -0400 Subject: [PATCH 006/196] [compiler][ez] Reference rc tag for install instructions (#7955) Updates our previous RC blogpost to point people to the `rc` tag, not the specific rc version --- src/content/blog/2025/04/21/react-compiler-rc.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/blog/2025/04/21/react-compiler-rc.md b/src/content/blog/2025/04/21/react-compiler-rc.md index ecbbb8747..2ebbf3bae 100644 --- a/src/content/blog/2025/04/21/react-compiler-rc.md +++ b/src/content/blog/2025/04/21/react-compiler-rc.md @@ -57,23 +57,23 @@ During the RC period, we encourage all React users to try the compiler and provi As noted in the Beta announcement, React Compiler is compatible with React 17 and up. If you are not yet on React 19, you can use React Compiler by specifying a minimum target in your compiler config, and adding `react-compiler-runtime` as a dependency. You can find docs on this [here](https://react.dev/learn/react-compiler#using-react-compiler-with-react-17-or-18). ## Migrating from eslint-plugin-react-compiler to eslint-plugin-react-hooks {/*migrating-from-eslint-plugin-react-compiler-to-eslint-plugin-react-hooks*/} -If you have already installed eslint-plugin-react-compiler, you can now remove it and use `eslint-plugin-react-hooks@6.0.0-rc.1`. Many thanks to [@michaelfaith](https://bsky.app/profile/michael.faith) for contributing to this improvement! +If you have already installed eslint-plugin-react-compiler, you can now remove it and use `eslint-plugin-react-hooks@rc`. Many thanks to [@michaelfaith](https://bsky.app/profile/michael.faith) for contributing to this improvement! To install: npm -{`npm install --save-dev eslint-plugin-react-hooks@6.0.0-rc.1`} +{`npm install --save-dev eslint-plugin-react-hooks@rc`} pnpm -{`pnpm add --save-dev eslint-plugin-react-hooks@6.0.0-rc.1`} +{`pnpm add --save-dev eslint-plugin-react-hooks@rc`} yarn -{`yarn add --dev eslint-plugin-react-hooks@6.0.0-rc.1`} +{`yarn add --dev eslint-plugin-react-hooks@rc`} ```js From 19c8201d0a1dc45bae45df7e0b5c9a38dda2df12 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 28 Aug 2025 18:14:32 -0400 Subject: [PATCH 007/196] [compiler] Update docs on eslint-plugin-react-hooks installation (#7956) The compiler rule is now enabled by default in 6.0.0-rc.2, so there is no longer a need to manually enable the compiler rule in user's eslint configs. --- src/content/learn/react-compiler/installation.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/content/learn/react-compiler/installation.md b/src/content/learn/react-compiler/installation.md index 3606c9c6d..a40b1f5af 100644 --- a/src/content/learn/react-compiler/installation.md +++ b/src/content/learn/react-compiler/installation.md @@ -176,16 +176,7 @@ Install the ESLint plugin: npm install -D eslint-plugin-react-hooks@rc -Then enable the compiler rule in your ESLint configuration: - -```js {3} -// .eslintrc.js -module.exports = { - rules: { - 'react-hooks/react-compiler': 'error', - }, -}; -``` +If you haven't already configured eslint-plugin-react-hooks, follow the [installation instructions in the readme](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md#installation). The compiler rule is enabled by default in the latest RC, so no additional configuration is needed. The ESLint rule will: - Identify violations of the [Rules of React](/reference/rules) From 2774ddfa0c39b8c2f0563b987dcb90a01ee723cf Mon Sep 17 00:00:00 2001 From: Ricky Date: Fri, 29 Aug 2025 11:00:24 -0400 Subject: [PATCH 008/196] Add reload button, rename reset to clear (#7954) --- next-env.d.ts | 3 ++- src/components/MDX/Sandpack/ClearButton.tsx | 22 +++++++++++++++++++ src/components/MDX/Sandpack/NavigationBar.tsx | 16 ++++++++------ .../{ResetButton.tsx => ReloadButton.tsx} | 13 ++++++----- 4 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 src/components/MDX/Sandpack/ClearButton.tsx rename src/components/MDX/Sandpack/{ResetButton.tsx => ReloadButton.tsx} (53%) diff --git a/next-env.d.ts b/next-env.d.ts index 52e831b43..3cd7048ed 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/src/components/MDX/Sandpack/ClearButton.tsx b/src/components/MDX/Sandpack/ClearButton.tsx new file mode 100644 index 000000000..868f9fb66 --- /dev/null +++ b/src/components/MDX/Sandpack/ClearButton.tsx @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import * as React from 'react'; +import {IconClose} from '../../Icon/IconClose'; +export interface ClearButtonProps { + onClear: () => void; +} + +export function ClearButton({onClear}: ClearButtonProps) { + return ( + + ); +} diff --git a/src/components/MDX/Sandpack/NavigationBar.tsx b/src/components/MDX/Sandpack/NavigationBar.tsx index bf2c3186c..d115868dd 100644 --- a/src/components/MDX/Sandpack/NavigationBar.tsx +++ b/src/components/MDX/Sandpack/NavigationBar.tsx @@ -17,7 +17,8 @@ import { useSandpackNavigation, } from '@codesandbox/sandpack-react/unstyled'; import {OpenInCodeSandboxButton} from './OpenInCodeSandboxButton'; -import {ResetButton} from './ResetButton'; +import {ReloadButton} from './ReloadButton'; +import {ClearButton} from './ClearButton'; import {DownloadButton} from './DownloadButton'; import {IconChevron} from '../../Icon/IconChevron'; import {Listbox} from '@headlessui/react'; @@ -95,7 +96,7 @@ export function NavigationBar({providedFiles}: {providedFiles: Array}) { // Note: in a real useEvent, onContainerResize would be omitted. }, [isMultiFile, onContainerResize]); - const handleReset = () => { + const handleClear = () => { /** * resetAllFiles must come first, otherwise * the previous content will appear for a second @@ -103,13 +104,13 @@ export function NavigationBar({providedFiles}: {providedFiles: Array}) { * * Plus, it should only prompt if there's any file changes */ - if ( - sandpack.editorState === 'dirty' && - confirm('Reset all your edits too?') - ) { + if (sandpack.editorState === 'dirty' && confirm('Clear all your edits?')) { sandpack.resetAllFiles(); } + refresh(); + }; + const handleReload = () => { refresh(); }; @@ -188,7 +189,8 @@ export function NavigationBar({providedFiles}: {providedFiles: Array}) { className="px-3 flex items-center justify-end text-start" translate="yes"> - + + {activeFile.endsWith('.tsx') && ( void; +export interface ReloadButtonProps { + onReload: () => void; } -export function ResetButton({onReset}: ResetButtonProps) { +export function ReloadButton({onReload}: ReloadButtonProps) { return ( ); } From ddfcf6e8a38f21c3d47cfed2822cd0bc41d68bfd Mon Sep 17 00:00:00 2001 From: SeungMin Shin Date: Tue, 2 Sep 2025 03:32:29 +0900 Subject: [PATCH 009/196] fix: typo in component style documentation (#7925) --- src/content/reference/react-dom/components/style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react-dom/components/style.md b/src/content/reference/react-dom/components/style.md index b98b6088c..561c207d1 100644 --- a/src/content/reference/react-dom/components/style.md +++ b/src/content/reference/react-dom/components/style.md @@ -49,7 +49,7 @@ React can move ` +
This card uses a font from the stylesheet.
+ + `); + doc.close(); + } + return ( + <> + + + From e730fd0dd71f3b26a9988cc5c1774dc7a39fa486 Mon Sep 17 00:00:00 2001 From: Nasser Date: Tue, 18 Aug 2026 21:45:59 +0200 Subject: [PATCH 191/196] Add cache, cacheSignal, and captureOwnerStack to the APIs overview (#8597) --- src/content/reference/react/apis.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/content/reference/react/apis.md b/src/content/reference/react/apis.md index 777b8fc7b..51438ad9b 100644 --- a/src/content/reference/react/apis.md +++ b/src/content/reference/react/apis.md @@ -15,6 +15,9 @@ In addition to [Hooks](/reference/react/hooks) and [Components](/reference/react * [`memo`](/reference/react/memo) lets your component skip re-renders with same props. Used with [`useMemo`](/reference/react/useMemo) and [`useCallback`.](/reference/react/useCallback) * [`startTransition`](/reference/react/startTransition) lets you mark a state update as non-urgent. Similar to [`useTransition`.](/reference/react/useTransition) * [`act`](/reference/react/act) lets you wrap renders and interactions in tests to ensure updates have processed before making assertions. +* [`cache`](/reference/react/cache) lets you cache the result of a data fetch or computation. +* [`cacheSignal`](/reference/react/cacheSignal) lets you know when the `cache()` lifetime is over. +* [`captureOwnerStack`](/reference/react/captureOwnerStack) reads the current Owner Stack in development and returns it as a string if available. --- From 96ad418a9a8f00fb63e7d6c56400384b37539138 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Wed, 19 Aug 2026 11:12:12 -0700 Subject: [PATCH 192/196] Document browser-only rendering (#8582) * Document browser-only rendering Add the Canary browser API reference, including optional lazy reasons, server bailout reporting, fatal and abort behavior, navigation entries, and onBrowserBailout options for every streaming, resume, and prerender API that supports it. * Polish browser API docs and add live example * Document browser with use and Suspense --------- Co-authored-by: Aurora Scharff --- src/content/reference/react-dom/browser.md | 293 ++++++++++++++++++ src/content/reference/react-dom/index.md | 6 + .../server/renderToPipeableStream.md | 1 + .../server/renderToReadableStream.md | 1 + .../reference/react-dom/server/resume.md | 1 + .../server/resumeToPipeableStream.md | 1 + .../reference/react-dom/static/prerender.md | 1 + .../react-dom/static/prerenderToNodeStream.md | 1 + .../react-dom/static/resumeAndPrerender.md | 1 + .../static/resumeAndPrerenderToNodeStream.md | 1 + src/content/reference/react/Suspense.md | 123 ++++++++ src/content/reference/react/use.md | 156 ++++++++++ src/sidebarReference.json | 5 + 13 files changed, 591 insertions(+) create mode 100644 src/content/reference/react-dom/browser.md diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md new file mode 100644 index 000000000..11f787d2c --- /dev/null +++ b/src/content/reference/react-dom/browser.md @@ -0,0 +1,293 @@ +--- +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 a 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. + +Press **Render the page**. The loading fallback appears first. After a short delay, React hydrates the page and displays the browser-only editor. + + + +```js src/App.js active +import { Suspense, use } from 'react'; +import { browser } from 'react-dom'; + +function BrowserOnlyEditor() { + use(browser('The editor requires browser APIs.')); + return ; +} + +export default function App() { + return ( + Loading editor...

}> + +
+ ); +} +``` + +```js src/Document.js hidden +import App from './App.js'; + +export default function Document() { + return ( + + + Article editor + + +

Article editor

+ + + + ); +} +``` + +```js src/index.js +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, ); +} + +const renderButton = document.getElementById('render'); +renderButton.addEventListener('click', () => { + renderButton.disabled = true; + main(document.getElementById('preview')); +}, { once: true }); +``` + +```js src/demo-helpers.js hidden +export async function flushReadableStreamToFrame(readable, frame) { + const doc = frame.contentWindow.document; + const decoder = new TextDecoder(); + for await (const chunk of readable) { + doc.write(decoder.decode(chunk, { stream: true })); + } + doc.close(); +} +``` + +```html public/index.html + + + + + Browser-only rendering + + + +

+ + + +``` + +```css src/styles.css hidden +iframe { + width: 100%; + height: 180px; + border: 1px solid #aaa; +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "canary", + "react-dom": "canary", + "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 } from 'react'; +import { browser } from 'react-dom'; + +export default function BrowserOnlyEditor() { + use(browser('The editor requires browser APIs.')); + return ; +} +``` + + + +--- + +### Conditionally rendering in the browser {/*conditionally-rendering-in-the-browser*/} + +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: + +```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}

; +} +``` + +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. + +--- + +### 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 } from 'react'; +import { browser } from 'react-dom'; +import { renderToPipeableStream } from 'react-dom/server'; + +function BrowserOnlyEditor() { + use(browser(() => new Error('The editor requires a browser API.'))); + return ; +} + +const { pipe } = renderToPipeableStream( + Loading editor...

}> + +
, + { + 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/index.md b/src/content/reference/react-dom/index.md index d01bd6562..6f1188442 100644 --- a/src/content/reference/react-dom/index.md +++ b/src/content/reference/react-dom/index.md @@ -30,6 +30,12 @@ These APIs can be used to make apps faster by pre-loading resources such as scri * [`preinit`](/reference/react-dom/preinit) lets you fetch and evaluate an external script or fetch and insert a stylesheet. * [`preinitModule`](/reference/react-dom/preinitModule) lets you fetch and evaluate an ESM module. +## 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. + --- ## Entry points {/*entry-points*/} diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md index 9668e01b9..5d48c6a66 100644 --- a/src/content/reference/react-dom/server/renderToPipeableStream.md +++ b/src/content/reference/react-dom/server/renderToPipeableStream.md @@ -56,6 +56,7 @@ On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to * **optional** `namespaceURI`: A string with the root [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) for the stream. Defaults to regular HTML. Pass `'http://www.w3.org/2000/svg'` for SVG or `'http://www.w3.org/1998/Math/MathML'` for MathML. * **optional** `nonce`: A [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) string to allow scripts for [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src). * **optional** `onAllReady`: A callback that fires when all rendering is complete, including both the [shell](#specifying-what-goes-into-the-shell) and all additional [content.](#streaming-more-content-as-it-loads) You can use this instead of `onShellReady` [for crawlers and static generation.](#waiting-for-all-content-to-load-for-crawlers-and-static-generation) If you start streaming here, you won't get any progressive loading. The stream will contain the final HTML. + * **optional** `onBrowserBailout`: A callback React calls when it recovers from [`browser()`](/reference/react-dom/browser) by leaving a Suspense fallback for the browser to replace. It receives an `Error` describing the browser-only render and an `errorInfo` object containing the `componentStack`. If a reason was passed to `browser`, it is available as `error.cause`. By default, React does nothing. [See how to report browser-only rendering.](/reference/react-dom/browser#reporting-browser-only-rendering-on-the-server) * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](#recovering-from-errors-outside-the-shell) or [not.](#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](#logging-crashes-on-the-server) make sure that you still call `console.error`. You can also use it to [adjust the status code](#setting-the-status-code) before the shell is emitted. * **optional** `onShellReady`: A callback that fires right after the [initial shell](#specifying-what-goes-into-the-shell) has been rendered. You can [set the status code](#setting-the-status-code) and call `pipe` here to start streaming. React will [stream the additional content](#streaming-more-content-as-it-loads) after the shell along with the inline `