diff --git a/.env.production b/.env.production
index d25eb7dd..e403f96b 100644
--- a/.env.production
+++ b/.env.production
@@ -1 +1 @@
-NEXT_PUBLIC_GA_TRACKING_ID = 'UA-41298772-4'
\ No newline at end of file
+NEXT_PUBLIC_GA_TRACKING_ID = 'G-B1E83PJ3RT'
\ No newline at end of file
diff --git a/package.json b/package.json
index 472ef79c..b5e07d70 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,6 @@
"classnames": "^2.2.6",
"date-fns": "^2.16.1",
"debounce": "^1.2.1",
- "ga-lite": "^2.1.4",
"github-slugger": "^1.3.0",
"next": "^13.4.1",
"next-remote-watch": "^1.0.0",
diff --git a/src/components/Layout/Feedback.tsx b/src/components/Layout/Feedback.tsx
index 2bf9afe5..86fc9135 100644
--- a/src/components/Layout/Feedback.tsx
+++ b/src/components/Layout/Feedback.tsx
@@ -4,7 +4,6 @@
import {useState} from 'react';
import {useRouter} from 'next/router';
-import {ga} from '../../utils/analytics';
export function Feedback({onSubmit = () => {}}: {onSubmit?: () => void}) {
const {asPath} = useRouter();
@@ -48,14 +47,12 @@ const thumbsDownIcon = (
function sendGAEvent(isPositive: boolean) {
// Fragile. Don't change unless you've tested the network payload
// and verified that the right events actually show up in GA.
- ga(
- 'send',
- 'event',
- 'button',
- 'feedback',
- window.location.pathname,
- isPositive ? '1' : '0'
- );
+ // @ts-ignore
+ gtag('event', 'feedback', {
+ event_category: 'button',
+ event_label: window.location.pathname,
+ value: isPositive ? 1 : 0,
+ });
}
function SendFeedback({onSubmit}: {onSubmit: () => void}) {
diff --git a/src/components/Layout/Page.tsx b/src/components/Layout/Page.tsx
index fb771f90..04876bab 100644
--- a/src/components/Layout/Page.tsx
+++ b/src/components/Layout/Page.tsx
@@ -28,7 +28,12 @@ interface PageProps {
children: React.ReactNode;
toc: Array;
routeTree: RouteItem;
- meta: {title?: string; canary?: boolean; description?: string};
+ meta: {
+ title?: string;
+ titleForTitleTag?: string;
+ canary?: boolean;
+ description?: string;
+ };
section: 'learn' | 'reference' | 'community' | 'blog' | 'home' | 'unknown';
}
@@ -107,6 +112,7 @@ export function Page({children, toc, routeTree, meta, section}: PageProps) {
<>
{
if (lintErrors.length === 0) {
diff --git a/src/components/Seo.tsx b/src/components/Seo.tsx
index 79f19f87..5af169e1 100644
--- a/src/components/Seo.tsx
+++ b/src/components/Seo.tsx
@@ -9,6 +9,7 @@ import {siteConfig} from '../siteConfig';
export interface SeoProps {
title: string;
+ titleForTitleTag: undefined | string;
description?: string;
image?: string;
// jsonld?: JsonLDType | Array;
@@ -36,7 +37,7 @@ function getDomain(languageCode: string): string {
export const Seo = withRouter(
({
title,
- description = 'The library for web and native user interfaces',
+ titleForTitleTag,
image = '/images/og-default.png',
router,
children,
@@ -47,14 +48,20 @@ export const Seo = withRouter(
const canonicalUrl = `https://${siteDomain}${
router.asPath.split(/[\?\#]/)[0]
}`;
- const pageTitle = isHomePage ? title : title + ' – React';
+ // Allow setting a different title for Google results
+ const pageTitle =
+ (titleForTitleTag ?? title) + (isHomePage ? '' : ' – React');
// Twitter's meta parser is not very good.
const twitterTitle = pageTitle.replace(/[<>]/g, '');
+ let description = isHomePage
+ ? 'React is the library for web and native user interfaces. Build user interfaces out of individual pieces called components written in JavaScript. React is designed to let you seamlessly combine components written by independent people, teams, and organizations.'
+ : 'The library for web and native user interfaces';
return (
{title != null && {pageTitle}}
- {description != null && (
+ {isHomePage && (
+ // Let Google figure out a good description for each page.
)}
diff --git a/src/content/learn/describing-the-ui.md b/src/content/learn/describing-the-ui.md
index f71ac690..43232933 100644
--- a/src/content/learn/describing-the-ui.md
+++ b/src/content/learn/describing-the-ui.md
@@ -545,13 +545,21 @@ React uses trees to model the relationships between components and modules.
A React render tree is a representation of the parent and child relationship between components.
-An example React render tree.
+
+
+An example React render tree.
+
+
Components near the top of the tree, near the root component, are considered top-level components. Components with no child components are leaf components. This categorization of components is useful for understanding data flow and rendering performance.
Modelling the relationship between JavaScript modules is another useful way to understand your app. We refer to it as a module dependency tree.
-An example module dependency tree.
+
+
+An example module dependency tree.
+
+
A dependency tree is often used by build tools to bundle all the relevant JavaScript code for the client to download and render. A large bundle size regresses user experience for React apps. Understanding the module dependency tree is helpful to debug such issues.
diff --git a/src/content/learn/reacting-to-input-with-state.md b/src/content/learn/reacting-to-input-with-state.md
index 522aa63a..29f60ca6 100644
--- a/src/content/learn/reacting-to-input-with-state.md
+++ b/src/content/learn/reacting-to-input-with-state.md
@@ -84,7 +84,7 @@ function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
- if (answer.toLowerCase() == 'istanbul') {
+ if (answer.toLowerCase() === 'istanbul') {
resolve();
} else {
reject(new Error('Good guess but a wrong answer. Try again!'));
diff --git a/src/content/learn/understanding-your-ui-as-a-tree.md b/src/content/learn/understanding-your-ui-as-a-tree.md
index 2a5a24b8..98f60cea 100644
--- a/src/content/learn/understanding-your-ui-as-a-tree.md
+++ b/src/content/learn/understanding-your-ui-as-a-tree.md
@@ -253,7 +253,7 @@ With conditional rendering, across different renders, the render tree may render
In this example, depending on what `inspiration.type` is, we may render `` or ``. The render tree may be different for each render pass.
-Although render trees may differ across render pases, these trees are generally helpful for identifying what the top-level and leaf components are in a React app. Top-level components are the components nearest to the root component and affect the rendering performance of all the components beneath them and often contain the most complexity. Leaf components are near the bottom of the tree and have no child components and are often frequently re-rendered.
+Although render trees may differ across render passes, these trees are generally helpful for identifying what the *top-level* and *leaf components* are in a React app. Top-level components are the components nearest to the root component and affect the rendering performance of all the components beneath them and often contain the most complexity. Leaf components are near the bottom of the tree and have no child components and are often frequently re-rendered.
Identifying these categories of components are useful for understanding data flow and performance of your app.
diff --git a/src/content/reference/react-dom/components/form.md b/src/content/reference/react-dom/components/form.md
new file mode 100644
index 00000000..7c602322
--- /dev/null
+++ b/src/content/reference/react-dom/components/form.md
@@ -0,0 +1,435 @@
+---
+title: "
` {/*form*/}
+
+To create interactive controls for submitting information, render the [built-in browser `
` supports all [common element props.](/reference/react-dom/components/common#props)
+
+[`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action): a URL or function. When a URL is passed to `action` the form will behave like the HTML form component. When a function is passed to `action` the function will handle the form submission. The function passed to `action` may be async and will be called with a single argument containing the [form data](https://developer.mozilla.org/en-US/docs/Web/API/FormData) of the submitted form. The `action` prop can be overridden by a `formAction` attribute on a `
}
+ >
+
+
+ Search
+
+
+ );
+}
+
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "18.3.0-canary-6db7f4209-20231021",
+ "react-dom": "18.3.0-canary-6db7f4209-20231021",
+ "react-scripts": "^5.0.0",
+ "react-error-boundary": "4.0.3"
+ },
+ "main": "/index.js",
+ "devDependencies": {}
+}
+```
+
+
+
+### Display a form submission error without JavaScript {/*display-a-form-submission-error-without-javascript*/}
+
+Displaying a form submission error message before the JavaScript bundle loads for progressive enhancement requires that:
+
+1. `
` be rendered by a [Server Component](/reference/react/use-client)
+1. the function passed to the `
`'s `action` prop be a [Server Action](/reference/react/use-server)
+1. the `useFormState` Hook be used to display the error message
+
+`useFormState` takes two parameters: a [Server Action](/reference/react/use-server) and an initial state. `useFormState` returns two values, a state variable and an action. The action returned by `useFormState` should be passed to the `action` prop of the form. The state variable returned by `useFormState` can be used to displayed an error message. The value returned by the [Server Action](/reference/react/use-server) passed to `useFormState` will be used to update the state variable.
+
+
+
+```js App.js
+import { useFormState } from "react-dom";
+import { signUpNewUser } from "./api";
+
+export default function Page() {
+ async function signup(prevState, formData) {
+ "use server";
+ const email = formData.get("email");
+ try {
+ await signUpNewUser(email);
+ alert(`Added "${email}"`);
+ } catch (err) {
+ return err.toString();
+ }
+ }
+ const [message, formAction] = useFormState(signup, null);
+ return (
+ <>
+
Signup for my newsletter
+
Signup with the same email twice to see an error
+
+
+
+ Sign up
+ {!!message &&
{message}
}
+
+ >
+ );
+}
+```
+
+```js api.js hidden
+let emails = [];
+
+export async function signUpNewUser(newEmail) {
+ if (emails.includes(newEmail)) {
+ throw new Error("This email address has already been added");
+ }
+ emails.push(newEmail);
+}
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "18.3.0-canary-6db7f4209-20231021",
+ "react-dom": "18.3.0-canary-6db7f4209-20231021",
+ "react-scripts": "^5.0.0"
+ },
+ "main": "/index.js",
+ "devDependencies": {}
+}
+```
+
+
+
+Learn more about updating state from a form action with the [`useFormState`](/reference/react-dom/hooks/useFormState) docs
+
+### Handling multiple submission types {/*handling-multiple-submission-types*/}
+
+Forms can be designed to handle multiple submission actions based on the button pressed by the user. Each button inside a form can be associated with a distinct action or behavior by setting the `formAction` prop.
+
+When a user taps a specific button, the form is submitted, and a corresponding action, defined by that button's attributes and action, is executed. For instance, a form might submit an article for review by default but have a separate button with `formAction` set to save the article as a draft.
+
+
+
+```js App.js
+export default function Search() {
+ function publish(formData) {
+ const content = formData.get("content");
+ const button = formData.get("button");
+ alert(`'${content}' was published with the '${button}' button`);
+ }
+
+ function save(formData) {
+ const content = formData.get("content");
+ alert(`Your draft of '${content}' has been saved!`);
+ }
+
+ return (
+
+
+
+ Publish
+ Save draft
+
+ );
+}
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "18.3.0-canary-6db7f4209-20231021",
+ "react-dom": "18.3.0-canary-6db7f4209-20231021",
+ "react-scripts": "^5.0.0"
+ },
+ "main": "/index.js",
+ "devDependencies": {}
+}
+```
+
+
diff --git a/src/content/reference/react-dom/components/input.md b/src/content/reference/react-dom/components/input.md
index 06010d30..7328fddc 100644
--- a/src/content/reference/react-dom/components/input.md
+++ b/src/content/reference/react-dom/components/input.md
@@ -32,6 +32,13 @@ To display an input, render the [built-in browser ``](https://developer.m
`` supports all [common element props.](/reference/react-dom/components/common#props)
+
+
+React's extensions to the `formAction` prop are currently only available in React's canary and experimental channels. In stable releases of React `formAction` works only as a [built-in browser HTML component](https://react.dev/reference/react-dom/components#all-html-components). Learn more about [React's release channels here](/community/versioning-policy#all-release-channels).
+
+
+[`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): A string or function. Overrides the parent `
` for `type="submit"` and `type="image"`. When a URL is passed to `action` the form will behave like a standard HTML form. When a function is passed to `formAction` the function will handle the form submission. See [`
`](/reference/react-dom/components/form#props).
+
You can [make an input controlled](#controlling-an-input-with-a-state-variable) by passing one of these props:
* [`checked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#checked): A boolean. For a checkbox input or a radio button, controls whether it is selected.
diff --git a/src/content/reference/react-dom/hooks/index.md b/src/content/reference/react-dom/hooks/index.md
index 6490dc11..937de808 100644
--- a/src/content/reference/react-dom/hooks/index.md
+++ b/src/content/reference/react-dom/hooks/index.md
@@ -1,5 +1,5 @@
---
-title: "React DOM Hooks"
+title: "Built-in React DOM Hooks"
---
@@ -21,7 +21,7 @@ Form Hooks are currently only available in React's canary and experimental chann
*Forms* let you create interactive controls for submitting information. To manage forms in your components, use one of these Hooks:
* [`useFormStatus`](/reference/react-dom/hooks/useFormStatus) allows you to make updates to the UI based on the status of the a form.
-* `useFormState` allows you to manage state inside a form.
+* [`useFormState`](/reference/react-dom/hooks/useFormState) allows you to manage state inside a form.
```js
function Form({ action }) {
@@ -46,3 +46,4 @@ function Button() {
);
}
```
+
diff --git a/src/content/reference/react-dom/hooks/useFormState.md b/src/content/reference/react-dom/hooks/useFormState.md
new file mode 100644
index 00000000..53c73ae3
--- /dev/null
+++ b/src/content/reference/react-dom/hooks/useFormState.md
@@ -0,0 +1,291 @@
+---
+title: useFormState
+canary: true
+---
+
+
+
+The `useFormState` Hook is currently only available in React's canary and experimental channels. Learn more about [release channels here](/community/versioning-policy#all-release-channels). In addition, you need to use a framework that supports [React Server Components](/reference/react/use-client) to get the full benefit of `useFormState`.
+
+
+
+
+
+`useFormState` is a Hook that allows you to update state based on the result of a form action.
+
+```js
+const [state, formAction] = useFormState(fn, initialState);
+```
+
+
+
+
+
+---
+
+## Reference {/*reference*/}
+
+### `useFormState(action, initialState)` {/*useformstate*/}
+
+{/* TODO T164397693: link to actions documentation once it exists */}
+
+Call `useFormState` at the top level of your component to create component state that is updated [when a form action is invoked](/reference/react-dom/components/form). You pass `useFormState` an existing form action function as well as an initial state, and it returns a new action that you use in your form, along with the latest form state. The latest form state is also passed to the function that you provided.
+
+```js
+import { useFormState } from "react-dom";
+
+async function increment(previousState, formData) {
+ return previousState + 1;
+}
+
+function StatefulForm({}) {
+ const [state, formAction] = useFormState(increment, 0);
+ return (
+
+ {state}
+ Increment
+
+ )
+}
+```
+
+The form state is the value returned by the action when the form was last submitted. If the form has not yet been submitted, it is the initial state that you pass.
+
+If used with a server action, `useFormState` allows the server's response from submitting the form to be shown even before hydration has completed.
+
+[See more examples below.](#usage)
+
+#### Parameters {/*parameters*/}
+
+* `fn`: The function to be called when the form is submitted or button pressed. When the function is called, it will receive the previous state of the form (initially the `initialState` that you pass, subsequently its previous return value) as its initial argument, followed by the arguments that a form action normally receives.
+* `initialState`: The value you want the state to be initially. It can be any serializable value. This argument is ignored after the action is first invoked.
+
+{/* TODO T164397693: link to serializable values docs once it exists */}
+
+#### Returns {/*returns*/}
+
+`useFormState` returns an array with exactly two values:
+
+1. The current state. During the first render, it will match the `initialState` you have passed. After the action is invoked, it will match the value returned by the action.
+2. A new action that you can pass as the `action` prop to your `form` component or `formAction` prop to any `button` component within the form.
+
+#### Caveats {/*caveats*/}
+
+* When used with a framework that supports React Server Components, `useFormState` lets you make forms interactive before JavaScript has executed on the client. When used without Server Components, it is equivalent to component local state.
+* The function passed to `useFormState` receives an extra argument, the previous or initial state, as its first argument. This makes its signature different than if it were used directly as a form action without using `useFormState`.
+
+---
+
+## Usage {/*usage*/}
+
+### Using information returned by a form action {/*using-information-returned-by-a-form-action*/}
+
+Call `useFormState` at the top level of your component to access the return value of an action from the last time a form was submitted.
+
+```js [[1, 5, "state"], [2, 5, "formAction"], [3, 5, "action"], [4, 5, "null"], [2, 8, "formAction"]]
+import { useFormState } from 'react-dom';
+import { action } from './actions.js';
+
+function MyComponent() {
+ const [state, formAction] = useFormState(action, null);
+ // ...
+ return (
+
+ {/* ... */}
+
+ );
+}
+```
+
+`useFormState` returns an array with exactly two items:
+
+1. The current state of the form, which is initially set to the initial state you provided, and after the form is submitted is set to the return value of the action you provided.
+2. A new action that you pass to `
` as its `action` prop.
+
+When the form is submitted, the action function that you provided will be called. Its return value will become the new current state of the form.
+
+The action that you provide will also receive a new first argument, namely the current state of the form. The first time the form is submitted, this will be the initial state you provided, while with subsequent submissions, it will be the return value from the last time the action was called. The rest of the arguments are the same as if `useFormState` had not been used
+
+```js [[3, 1, "action"], [1, 1, "currentState"]]
+function action(currentState, formData) {
+ // ...
+ return 'next state';
+}
+```
+
+
+
+#### Display form errors {/*display-form-errors*/}
+
+To display messages such as an error message or toast that's returned by a server action, wrap the action in a call to `useFormState`.
+
+
+
+```js App.js
+import { useState } from "react";
+import { useFormState } from "react-dom";
+import { addToCart } from "./actions.js";
+
+function AddToCartForm({itemID, itemTitle}) {
+ const [message, formAction] = useFormState(addToCart, null);
+ return (
+
+
{itemTitle}
+
+ Add to Cart
+ {message}
+
+ );
+}
+
+export default function App() {
+ return (
+ <>
+
+
+ >
+ )
+}
+```
+
+```js actions.js
+"use server";
+
+export async function addToCart(prevState, queryData) {
+ const itemID = queryData.get('itemID');
+ if (itemID === "1") {
+ return "Added to cart";
+ } else {
+ return "Couldn't add to cart: the item is sold out.";
+ }
+}
+```
+
+```css styles.css hidden
+form {
+ border: solid 1px black;
+ margin-bottom: 24px;
+ padding: 12px
+}
+
+form button {
+ margin-right: 12px;
+}
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "canary",
+ "react-dom": "canary",
+ "react-scripts": "^5.0.0"
+ },
+ "main": "/index.js",
+ "devDependencies": {}
+}
+```
+
+
+
+
+#### Display structured information after submitting a form {/*display-structured-information-after-submitting-a-form*/}
+
+The return value from a server action can be any serializable value. For example, it could be an object that includes a boolean indicating whether the action was successful, an error message, or updated information.
+
+
+
+```js App.js
+import { useState } from "react";
+import { useFormState } from "react-dom";
+import { addToCart } from "./actions.js";
+
+function AddToCartForm({itemID, itemTitle}) {
+ const [formState, formAction] = useFormState(addToCart, {});
+ return (
+
+
{itemTitle}
+
+ Add to Cart
+ {formState?.success &&
+
+ Added to cart! Your cart now has {formState.cartSize} items.
+
+ }
+ {formState?.success === false &&
+
+ Failed to add to cart: {formState.message}
+
+ }
+
+ );
+}
+
+export default function App() {
+ return (
+ <>
+
+
+ >
+ )
+}
+```
+
+```js actions.js
+"use server";
+
+export async function addToCart(prevState, queryData) {
+ const itemID = queryData.get('itemID');
+ if (itemID === "1") {
+ return {
+ success: true,
+ cartSize: 12,
+ };
+ } else {
+ return {
+ success: false,
+ message: "The item is sold out.",
+ };
+ }
+}
+```
+
+```css styles.css hidden
+form {
+ border: solid 1px black;
+ margin-bottom: 24px;
+ padding: 12px
+}
+
+form button {
+ margin-right: 12px;
+}
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "canary",
+ "react-dom": "canary",
+ "react-scripts": "^5.0.0"
+ },
+ "main": "/index.js",
+ "devDependencies": {}
+}
+```
+
+
+
+
+
+
+## Troubleshooting {/*troubleshooting*/}
+
+### My action can no longer read the submitted form data {/*my-action-can-no-longer-read-the-submitted-form-data*/}
+
+When you wrap an action with `useFormState`, it gets an extra argument *as its first argument*. The submitted form data is therefore its *second* argument instead of its first as it would usually be. The new first argument that gets added is the current state of the form.
+
+```js
+function action(currentState, formData) {
+ // ...
+}
+```
diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md
index 26422f18..20a5960e 100644
--- a/src/content/reference/react-dom/server/renderToPipeableStream.md
+++ b/src/content/reference/react-dom/server/renderToPipeableStream.md
@@ -431,7 +431,7 @@ function ProfilePage() {
}
```
-If an error happens in the `Posts` component or somewhere inside it, React will [try to recover from it:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-server-only-content)
+If an error happens in the `Posts` component or somewhere inside it, React will [try to recover from it:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content)
1. It will emit the loading fallback for the closest `` boundary (`PostsGlimmer`) into the HTML.
2. It will "give up" on trying to render the `Posts` content on the server anymore.
diff --git a/src/content/reference/react-dom/server/renderToReadableStream.md b/src/content/reference/react-dom/server/renderToReadableStream.md
index f4ed54ce..f407f224 100644
--- a/src/content/reference/react-dom/server/renderToReadableStream.md
+++ b/src/content/reference/react-dom/server/renderToReadableStream.md
@@ -435,7 +435,7 @@ function ProfilePage() {
}
```
-If an error happens in the `Posts` component or somewhere inside it, React will [try to recover from it:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-server-only-content)
+If an error happens in the `Posts` component or somewhere inside it, React will [try to recover from it:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content)
1. It will emit the loading fallback for the closest `` boundary (`PostsGlimmer`) into the HTML.
2. It will "give up" on trying to render the `Posts` content on the server anymore.
diff --git a/src/content/reference/react/experimental_taintObjectReference.md b/src/content/reference/react/experimental_taintObjectReference.md
index e3fd35a4..335f659c 100644
--- a/src/content/reference/react/experimental_taintObjectReference.md
+++ b/src/content/reference/react/experimental_taintObjectReference.md
@@ -64,7 +64,7 @@ experimental_taintObjectReference(
#### Caveats {/*caveats*/}
-- Recreating or cloning a tainted object creates a new untained object which main contain sensetive data. For example, if you have a tainted `user` object, `const userInfo = {name: user.name, ssn: user.ssn}` or `{...user}` will create new objects which are not tainted. `taintObjectReference` only protects against simple mistakes when the object is passed through to a Client Component unchanged.
+- Recreating or cloning a tainted object creates a new untained object which may contain sensitive data. For example, if you have a tainted `user` object, `const userInfo = {name: user.name, ssn: user.ssn}` or `{...user}` will create new objects which are not tainted. `taintObjectReference` only protects against simple mistakes when the object is passed through to a Client Component unchanged.
@@ -78,7 +78,7 @@ experimental_taintObjectReference(
### Prevent user data from unintentionally reaching the client {/*prevent-user-data-from-unintentionally-reaching-the-client*/}
-A Client Component should never accept objects that carry sensitive data. Ideally, the data fetching functions should not expose data that the current user should not have access to. Sometimes mistakes happen during refactoring. To protect against this mistakes happening down the line we can "taint" the user object in our data API.
+A Client Component should never accept objects that carry sensitive data. Ideally, the data fetching functions should not expose data that the current user should not have access to. Sometimes mistakes happen during refactoring. To protect against these mistakes happening down the line we can "taint" the user object in our data API.
```js
import {experimental_taintObjectReference} from 'react';
diff --git a/src/content/reference/react/experimental_taintUniqueValue.md b/src/content/reference/react/experimental_taintUniqueValue.md
index a67eebf7..aeee7456 100644
--- a/src/content/reference/react/experimental_taintUniqueValue.md
+++ b/src/content/reference/react/experimental_taintUniqueValue.md
@@ -67,7 +67,8 @@ experimental_taintUniqueValue(
#### Caveats {/*caveats*/}
-- Deriving new values from tainted values can compromise tainting protection. New values created by uppercasing tainted values, concatenating tainted string values into a larger string, converting tainted values to base64, substringing tainted values, and other similar transformations are not tainted unless you explicity call `taintUniqueValue` on these newly created values.
+* Deriving new values from tainted values can compromise tainting protection. New values created by uppercasing tainted values, concatenating tainted string values into a larger string, converting tainted values to base64, substringing tainted values, and other similar transformations are not tainted unless you explicitly call `taintUniqueValue` on these newly created values.
+* Do not use `taintUniqueValue` to protect low-entropy values such as PIN codes or phone numbers. If any value in a request is controlled by an attacker, they could infer which value is tainted by enumerating all possible values of the secret.
---
diff --git a/src/content/reference/react/use-client.md b/src/content/reference/react/use-client.md
index f0510415..b1ba1d7a 100644
--- a/src/content/reference/react/use-client.md
+++ b/src/content/reference/react/use-client.md
@@ -1,5 +1,6 @@
---
title: "'use client'"
+titleForTitleTag: "'use client' directive"
canary: true
---
diff --git a/src/content/reference/react/use-server.md b/src/content/reference/react/use-server.md
index 4acea191..cc271669 100644
--- a/src/content/reference/react/use-server.md
+++ b/src/content/reference/react/use-server.md
@@ -1,5 +1,6 @@
---
title: "'use server'"
+titleForTitleTag: "'use server' directive"
canary: true
---
diff --git a/src/content/reference/react/useEffect.md b/src/content/reference/react/useEffect.md
index 46f651a8..e6e4da10 100644
--- a/src/content/reference/react/useEffect.md
+++ b/src/content/reference/react/useEffect.md
@@ -1047,7 +1047,7 @@ Writing `fetch` calls inside Effects is a [popular way to fetch data](https://ww
This list of downsides is not specific to React. It applies to fetching data on mount with any library. Like with routing, data fetching is not trivial to do well, so we recommend the following approaches:
- **If you use a [framework](/learn/start-a-new-react-project#production-grade-react-frameworks), use its built-in data fetching mechanism.** Modern React frameworks have integrated data fetching mechanisms that are efficient and don't suffer from the above pitfalls.
-- **Otherwise, consider using or building a client-side cache.** Popular open source solutions include [React Query](https://react-query.tanstack.com/), [useSWR](https://swr.vercel.app/), and [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) You can build your own solution too, in which case you would use Effects under the hood but also add logic for deduplicating requests, caching responses, and avoiding network waterfalls (by preloading data or hoisting data requirements to routes).
+- **Otherwise, consider using or building a client-side cache.** Popular open source solutions include [React Query](https://tanstack.com/query/latest/), [useSWR](https://swr.vercel.app/), and [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) You can build your own solution too, in which case you would use Effects under the hood but also add logic for deduplicating requests, caching responses, and avoiding network waterfalls (by preloading data or hoisting data requirements to routes).
You can continue fetching data directly in Effects if neither of these approaches suit you.
diff --git a/src/content/reference/react/useLayoutEffect.md b/src/content/reference/react/useLayoutEffect.md
index 5af3ec5a..1b65ce3b 100644
--- a/src/content/reference/react/useLayoutEffect.md
+++ b/src/content/reference/react/useLayoutEffect.md
@@ -732,7 +732,7 @@ However, if you're running into this problem, you have a few different options:
- Replace `useLayoutEffect` with [`useEffect`.](/reference/react/useEffect) This tells React that it's okay to display the initial render result without blocking the paint (because the original HTML will become visible before your Effect runs).
-- Alternatively, [mark your component as client-only.](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-server-only-content) This tells React to replace its content up to the closest [``](/reference/react/Suspense) boundary with a loading fallback (for example, a spinner or a glimmer) during server rendering.
+- Alternatively, [mark your component as client-only.](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) This tells React to replace its content up to the closest [``](/reference/react/Suspense) boundary with a loading fallback (for example, a spinner or a glimmer) during server rendering.
- Alternatively, you can render a component with `useLayoutEffect` only after hydration. Keep a boolean `isMounted` state that's initialized to `false`, and set it to `true` inside a `useEffect` call. Your rendering logic can then be like `return isMounted ? : `. On the server and during the hydration, the user will see `FallbackContent` which should not call `useLayoutEffect`. Then React will replace it with `RealContent` which runs on the client only and can include `useLayoutEffect` calls.
diff --git a/src/content/reference/react/useOptimistic.md b/src/content/reference/react/useOptimistic.md
new file mode 100644
index 00000000..d0511b1a
--- /dev/null
+++ b/src/content/reference/react/useOptimistic.md
@@ -0,0 +1,145 @@
+---
+title: useOptimistic
+canary: true
+---
+
+
+
+The `useOptimistic` Hook 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).
+
+
+
+
+
+`useOptimistic` is a React Hook that lets you optimistically update the UI.
+
+```js
+ const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
+```
+
+
+
+
+
+---
+
+## Reference {/*reference*/}
+
+### `useOptimistic(state, updateFn)` {/*use*/}
+
+`useOptimistic` is a React hook that lets you show a different state while an async action is underway. It accepts some state as an argument and returns a copy of that state that can be different during the duration of an async action such as a network request. You provide a function that takes the current state and the input to the action, and returns the optimistic state to be used while the action is pending.
+
+This state is called the "optimistic" state because it is usually used to immediately present the user with the result of performing an action, even though the action actually takes time to complete.
+
+```js
+import { useOptimistic } from 'react';
+
+function AppContainer() {
+ const [optimisticState, addOptimistic] = useOptimistic(
+ state,
+ // updateFn
+ (currentState, optimisticValue) => {
+ // merge and return new state
+ // with optimistic value
+ }
+ );
+}
+```
+
+[See more examples below.](#usage)
+
+#### Parameters {/*parameters*/}
+
+* `state`: the value to be returned initially and whenever no action is pending.
+* `updateFn(currentState, optimisticValue)`: a function that takes the current state and the optimistic value passed to `addOptimistic` and returns the resulting optimistic state. It must be a pure function. `updateFn` takes in two parameters. The `currentState` and the `optimisticValue`. The return value will be the merged value of the `currentState` and `optimisticValue`.
+
+
+#### Returns {/*returns*/}
+
+* `optimisticState`: The resulting optimistic state. It is equal to `state` unless an action is pending, in which case it is equal to the value returned by `updateFn`.
+* `addOptimistic`: `addOptimistic` is the dispatching function to call when you have an optimistic update. It takes one argument, `optimisticValue`, of any type and will call the `updateFn` with `state` and `optimisticValue`.
+
+---
+
+## Usage {/*usage*/}
+
+### Optimistically updating forms {/*optimistically-updating-with-forms*/}
+
+The `useOptimistic` Hook provides a way to optimistically update the user interface before a background operation, like a network request, completes. In the context of forms, this technique helps to make apps feel more responsive. When a user submits a form, instead of waiting for the server's response to reflect the changes, the interface is immediately updated with the expected outcome.
+
+For example, when a user types a message into the form and hits the "Send" button, the `useOptimistic` Hook allows the message to immediately appear in the list with a "Sending..." label, even before the message is actually sent to a server. This "optimistic" approach gives the impression of speed and responsiveness. The form then attempts to truly send the message in the background. Once the server confirms the message has been received, the "Sending..." label is removed.
+
+
+
+
+```js App.js
+import { useOptimistic, useState, useRef } from "react";
+import { deliverMessage } from "./actions.js";
+
+function Thread({ messages, sendMessage }) {
+ const formRef = useRef();
+ async function formAction(formData) {
+ addOptimisticMessage(formData.get("message"));
+ formRef.current.reset();
+ await sendMessage(formData);
+ }
+ const [optimisticMessages, addOptimisticMessage] = useOptimistic(
+ messages,
+ (state, newMessage) => [
+ ...state,
+ {
+ text: newMessage,
+ sending: true
+ }
+ ]
+ );
+
+ return (
+ <>
+ {optimisticMessages.map((message, index) => (
+