{title}
- {canary && (
+ {version === 'canary' && (
+ )}
+ {version === 'experimental' && (
+
)}
diff --git a/src/components/Seo.tsx b/src/components/Seo.tsx
index 628085744..b8a8394b6 100644
--- a/src/components/Seo.tsx
+++ b/src/components/Seo.tsx
@@ -124,7 +124,14 @@ export const Seo = withRouter(
)}
+
+
+### React Compiler is now in RC! {/*react-compiler-is-now-in-rc*/}
+
+Please see the [RC blog post](/blog/2025/04/21/react-compiler-rc) for details.
+
+
+
The React team is excited to share new updates:
diff --git a/src/content/blog/2025/04/21/react-compiler-rc.md b/src/content/blog/2025/04/21/react-compiler-rc.md
new file mode 100644
index 000000000..ecbbb8747
--- /dev/null
+++ b/src/content/blog/2025/04/21/react-compiler-rc.md
@@ -0,0 +1,128 @@
+---
+title: "React Compiler RC"
+author: Lauren Tan and Mofei Zhang
+date: 2025/04/21
+description: We are releasing the compiler's first Release Candidate (RC) today.
+
+---
+
+April 21, 2025 by [Lauren Tan](https://x.com/potetotes) and [Mofei Zhang](https://x.com/zmofei).
+
+---
+
+
+
+The React team is excited to share new updates:
+
+
+
+1. We're publishing React Compiler RC today, in preparation of the compiler's stable release.
+2. We're merging `eslint-plugin-react-compiler` into `eslint-plugin-react-hooks`.
+3. We've added support for swc and are working with oxc to support Babel-free builds.
+
+---
+
+[React Compiler](https://react.dev/learn/react-compiler) is a build-time tool that optimizes your React app through automatic memoization. Last year, we published React Compiler’s [first beta](https://react.dev/blog/2024/10/21/react-compiler-beta-release) and received lots of great feedback and contributions. We’re excited about the wins we’ve seen from folks adopting the compiler (see case studies from [Sanity Studio](https://github.com/reactwg/react-compiler/discussions/33) and [Wakelet](https://github.com/reactwg/react-compiler/discussions/52)) and are working towards a stable release.
+
+We are releasing the compiler's first Release Candidate (RC) today. The RC is intended to be a stable and near-final version of the compiler, and safe to try out in production.
+
+## Use React Compiler RC today {/*use-react-compiler-rc-today*/}
+To install the RC:
+
+npm
+
+{`npm install --save-dev --save-exact babel-plugin-react-compiler@rc`}
+
+
+pnpm
+
+{`pnpm add --save-dev --save-exact babel-plugin-react-compiler@rc`}
+
+
+yarn
+
+{`yarn add --dev --exact babel-plugin-react-compiler@rc`}
+
+
+As part of the RC, we've been making React Compiler easier to add to your projects and added optimizations to how the compiler generates memoization. React Complier now supports optional chains and array indices as dependencies. We're exploring how to infer even more dependencies like equality checks and string interpolation. These improvements ultimately result in fewer re-renders and more responsive UIs.
+
+We have also heard from the community that the ref-in-render validation sometimes has false positives. Since as a general philosophy we want you to be able to fully trust in the compiler's error messages and hints, we are turning it off by default for now. We will keep working to improve this validation, and we will re-enable it in a follow up release.
+
+You can find more details on using the Compiler in [our docs](https://react.dev/learn/react-compiler).
+
+## Feedback {/*feedback*/}
+During the RC period, we encourage all React users to try the compiler and provide feedback in the React repo. Please [open an issue](https://github.com/facebook/react/issues) if you encounter any bugs or unexpected behavior. If you have a general question or suggestion, please post them in the [React Compiler Working Group](https://github.com/reactwg/react-compiler/discussions).
+
+## Backwards Compatibility {/*backwards-compatibility*/}
+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!
+
+To install:
+
+npm
+
+{`npm install --save-dev eslint-plugin-react-hooks@6.0.0-rc.1`}
+
+
+pnpm
+
+{`pnpm add --save-dev eslint-plugin-react-hooks@6.0.0-rc.1`}
+
+
+yarn
+
+{`yarn add --dev eslint-plugin-react-hooks@6.0.0-rc.1`}
+
+
+```js
+// eslint.config.js
+import * as reactHooks from 'eslint-plugin-react-hooks';
+
+export default [
+ // Flat Config (eslint 9+)
+ reactHooks.configs.recommended,
+
+ // Legacy Config
+ reactHooks.configs['recommended-latest']
+];
+```
+
+To enable the React Compiler rule, add `'react-hooks/react-compiler': 'error'` to your ESLint configuration.
+
+The linter does not require the compiler to be installed, so there's no risk in upgrading eslint-plugin-react-hooks. We recommend everyone upgrade today.
+
+## swc support (experimental) {/*swc-support-experimental*/}
+React Compiler can be installed across [several build tools](/learn/react-compiler#installation) such as Babel, Vite, and Rsbuild.
+
+In addition to those tools, we have been collaborating with Kang Dongyoon ([@kdy1dev](https://x.com/kdy1dev)) from the [swc](https://swc.rs/) team on adding additional support for React Compiler as an swc plugin. While this work isn't done, Next.js build performance should now be considerably faster when the [React Compiler is enabled in your Next.js app](https://nextjs.org/docs/app/api-reference/config/next-config-js/reactCompiler).
+
+We recommend using Next.js [15.3.1](https://github.com/vercel/next.js/releases/tag/v15.3.1) or greater to get the best build performance.
+
+Vite users can continue to use [vite-plugin-react](https://github.com/vitejs/vite-plugin-react) to enable the compiler, by adding it as a [Babel plugin](https://react.dev/learn/react-compiler#usage-with-vite). We are also working with the [oxc](https://oxc.rs/) team to [add support for the compiler](https://github.com/oxc-project/oxc/issues/10048). Once [rolldown](https://github.com/rolldown/rolldown) is officially released and supported in Vite and oxc support is added for React Compiler, we'll update the docs with information on how to migrate.
+
+## Upgrading React Compiler {/*upgrading-react-compiler*/}
+React Compiler works best when the auto-memoization applied is strictly for performance. Future versions of the compiler may change how memoization is applied, for example it could become more granular and precise.
+
+However, because product code may sometimes break the [rules of React](https://react.dev/reference/rules) in ways that aren't always statically detectable in JavaScript, changing memoization can occasionally have unexpected results. For example, a previously memoized value might be used as a dependency for a useEffect somewhere in the component tree. Changing how or whether this value is memoized can cause over or under-firing of that useEffect. While we encourage [useEffect only for synchronization](https://react.dev/learn/synchronizing-with-effects), your codebase may have useEffects that cover other use-cases such as effects that needs to only run in response to specific values changing.
+
+In other words, changing memoization may under rare circumstances cause unexpected behavior. For this reason, we recommend following the Rules of React and employing continuous end-to-end testing of your app so you can upgrade the compiler with confidence and identify any rules of React violations that might cause issues.
+
+If you don't have good test coverage, we recommend pinning the compiler to an exact version (eg `19.1.0`) rather than a SemVer range (eg `^19.1.0`). You can do this by passing the `--save-exact` (npm/pnpm) or `--exact` flags (yarn) when upgrading the compiler. You should then do any upgrades of the compiler manually, taking care to check that your app still works as expected.
+
+## Roadmap to Stable {/*roadmap-to-stable*/}
+*This is not a final roadmap, and is subject to change.*
+
+After a period of final feedback from the community on the RC, we plan on a Stable Release for the compiler.
+
+* ✅ Experimental: Released at React Conf 2024, primarily for feedback from application developers.
+* ✅ Public Beta: Available today, for feedback from library authors.
+* ✅ Release Candidate (RC): React Compiler works for the majority of rule-following apps and libraries without issue.
+* General Availability: After final feedback period from the community.
+
+Post-Stable, we plan to add more compiler optimizations and improvements. This includes both continual improvements to automatic memoization, and new optimizations altogether, with minimal to no change of product code. Each upgrade will continue to improve performance and add better handling of diverse JavaScript and React patterns.
+
+---
+
+Thanks to [Joe Savona](https://x.com/en_JS), [Jason Bonta](https://x.com/someextent), [Jimmy Lai](https://x.com/feedthejim), and [Kang Dongyoon](https://x.com/kdy1dev) (@kdy1dev) for reviewing and editing this post.
diff --git a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md
new file mode 100644
index 000000000..e4bb25a4a
--- /dev/null
+++ b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md
@@ -0,0 +1,14358 @@
+---
+title: "React Labs: View Transitions, Activity, and more"
+author: Ricky Hanlon
+date: 2025/04/23
+description: In React Labs posts, we write about projects in active research and development. In this post, we're sharing two new experimental features that are ready to try today, and updates on other areas we're working on now.
+---
+
+April 23, 2025 by [Ricky Hanlon](https://twitter.com/rickhanlonii)
+
+---
+
+
+
+In React Labs posts, we write about projects in active research and development. In this post, we're sharing two new experimental features that are ready to try today, and updates on other areas we're working on now.
+
+
+
+
+
+
+React Conf 2025 is scheduled for October 7–8 in Henderson, Nevada!
+
+We're looking for speakers to help us create talks about the features covered in this post. If you're interested in speaking at ReactConf, [please apply here](https://forms.reform.app/react-conf/call-for-speakers/) (no talk proposal required).
+
+For more info on tickets, free streaming, sponsoring, and more, see [the React Conf website](https://conf.react.dev).
+
+
+
+Today, we're excited to release documentation for two new experimental features that are ready for testing:
+
+- [View Transitions](#view-transitions)
+- [Activity](#activity)
+
+We're also sharing updates on new features currently in development:
+- [React Performance Tracks](#react-performance-tracks)
+- [Compiler IDE Extension](#compiler-ide-extension)
+- [Automatic Effect Dependencies](#automatic-effect-dependencies)
+- [Fragment Refs](#fragment-refs)
+- [Concurrent Stores](#concurrent-stores)
+
+---
+
+# New Experimental Features {/*new-experimental-features*/}
+
+View Transitions and Activity are now ready for testing in `react@experimental`. These features have been tested in production and are stable, but the final API may still change as we incorporate feedback.
+
+You can try them by upgrading React packages to the most recent experimental version:
+
+- `react@experimental`
+- `react-dom@experimental`
+
+Read on to learn how to use these features in your app, or check out the newly published docs:
+
+- [``](/reference/react/ViewTransition): A component that lets you activate an animation for a Transition.
+- [`addTransitionType`](/reference/react/addTransitionType): A function that allows you to specify the cause of a Transition.
+- [``](/reference/react/Activity): A component that lets you hide and show parts of the UI.
+
+## View Transitions {/*view-transitions*/}
+
+React View Transitions are a new experimental feature that makes it easier to add animations to UI transitions in your app. Under-the-hood, these animations use the new [`startViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition) API available in most modern browsers.
+
+To opt-in to animating an element, wrap it in the new `` component:
+
+```js
+// "what" to animate.
+
+
animate me
+
+```
+
+This new component lets you declaratively define "what" to animate when an animation is activated.
+
+You can define "when" to animate by using one of these three triggers for a View Transition:
+
+```js
+// "when" to animate.
+
+// Transitions
+startTransition(() => setState(...));
+
+// Deferred Values
+const deferred = useDeferredValue(value);
+
+// Suspense
+}>
+
Loading...
+
+```
+
+By default, these animations use the [default CSS animations for View Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API/Using#customizing_your_animations) applied (typically a smooth cross-fade). You can use [view transition pseudo-selectors](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API/Using#the_view_transition_pseudo-element_tree) to define "how" the animation runs. For example, you can use `*` to change the default animation for all transitions:
+
+```
+// "how" to animate.
+::view-transition-old(*) {
+ animation: 300ms ease-out fade-out;
+}
+::view-transition-new(*) {
+ animation: 300ms ease-in fade-in;
+}
+```
+
+When the DOM updates due to an animation trigger—like `startTransition`, `useDeferredValue`, or a `Suspense` fallback switching to content—React will use [declarative heuristics](/reference/react/ViewTransition#viewtransition) to automatically determine which `` components to activate for the animation. The browser will then run the animation that's defined in CSS.
+
+If you're familiar with the browser's View Transition API and want to know how React supports it, check out [How does `` Work](/reference/react/ViewTransition#how-does-viewtransition-work) in the docs.
+
+In this post, let's take a look at a few examples of how to use View Transitions.
+
+We'll start with this app, which doesn't animate any of the following interactions:
+- Click a video to view the details.
+- Click "back" to go back to the feed.
+- Type in the list to filter the videos.
+
+
+
+```js src/App.js active
+import TalkDetails from './Details'; import Home from './Home'; import {useRouter} from './router';
+
+export default function App() {
+ const {url} = useRouter();
+
+ // 🚩This version doesn't include any animations yet
+ return url === '/' ? : ;
+}
+```
+
+```js src/Details.js
+import { fetchVideo, fetchVideoDetails } from "./data";
+import { Thumbnail, VideoControls } from "./Videos";
+import { useRouter } from "./router";
+import Layout from "./Layout";
+import { use, Suspense } from "react";
+import { ChevronLeft } from "./Icons";
+
+function VideoInfo({ id }) {
+ const details = use(fetchVideoDetails(id));
+ return (
+ <>
+
+ {/* Opt-out of ViewTransition for the content. */}
+ {/* Content can define it's own ViewTransition. */}
+
+
+
{children}
+
+
+
+ );
+}
+```
+
+```js src/LikeButton.js hidden
+import {useState} from 'react';
+import {Heart} from './Icons';
+
+// A hack since we don't actually have a backend.
+// Unlike local state, this survives videos being filtered.
+const likedVideos = new Set();
+
+export default function LikeButton({video}) {
+ const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
+ const [animate, setAnimate] = useState(false);
+ return (
+
+ );
+}
+```
+
+```js src/Videos.js active
+import { useState, unstable_ViewTransition as ViewTransition } from "react"; import LikeButton from "./LikeButton"; import { useRouter } from "./router"; import { PauseIcon, PlayIcon } from "./Icons"; import { startTransition } from "react";
+
+export function Thumbnail({ video, children }) {
+ // Add a name to animate with a shared element transition.
+ // This uses the default animation, no additional css needed.
+ return (
+
+
+
+ );
+}
+
+```
+
+```js src/Icons.js hidden
+export function ChevronLeft() {
+ return (
+
+ );
+}
+
+export function PauseIcon() {
+ return (
+
+ );
+}
+
+export function PlayIcon() {
+ return (
+
+ );
+}
+export function Heart({liked, animate}) {
+ return (
+ <>
+
+
+
+ >
+ );
+}
+
+export function IconSearch(props) {
+ return (
+
+ );
+}
+```
+
+```js src/Layout.js active
+import {unstable_ViewTransition as ViewTransition} from 'react'; import { useIsNavPending } from "./router";
+
+export default function Page({ heading, children }) {
+ const isPending = useIsNavPending();
+ return (
+
+
+
+ {/* Custom classes based on transition type. */}
+
+ {heading}
+
+ {isPending && }
+
+
+ {/* Opt-out of ViewTransition for the content. */}
+ {/* Content can define it's own ViewTransition. */}
+
+
+
{children}
+
+
+
+ );
+}
+```
+
+```js src/LikeButton.js hidden
+import {useState} from 'react';
+import {Heart} from './Icons';
+
+// A hack since we don't actually have a backend.
+// Unlike local state, this survives videos being filtered.
+const likedVideos = new Set();
+
+export default function LikeButton({video}) {
+ const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
+ const [animate, setAnimate] = useState(false);
+ return (
+
+ );
+}
+```
+
+```js src/Videos.js hidden
+import { useState, unstable_ViewTransition as ViewTransition } from "react";
+import LikeButton from "./LikeButton";
+import { useRouter } from "./router";
+import { PauseIcon, PlayIcon } from "./Icons";
+import { startTransition } from "react";
+
+export function Thumbnail({ video, children }) {
+ // Add a name to animate with a shared element transition.
+ // This uses the default animation, no additional css needed.
+ return (
+
+
+ {/* Custom classes based on transition type. */}
+
+ {heading}
+
+ {isPending && }
+
+
+ {/* Opt-out of ViewTransition for the content. */}
+ {/* Content can define it's own ViewTransition. */}
+
+
+
{children}
+
+
+
+ );
+}
+```
+
+```js src/LikeButton.js hidden
+import {useState} from 'react';
+import {Heart} from './Icons';
+
+// A hack since we don't actually have a backend.
+// Unlike local state, this survives videos being filtered.
+const likedVideos = new Set();
+
+export default function LikeButton({video}) {
+ const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
+ const [animate, setAnimate] = useState(false);
+ return (
+
+ );
+}
+```
+
+```js src/Videos.js hidden
+import { useState, unstable_ViewTransition as ViewTransition } from "react";
+import LikeButton from "./LikeButton";
+import { useRouter } from "./router";
+import { PauseIcon, PlayIcon } from "./Icons";
+import { startTransition } from "react";
+
+export function Thumbnail({ video, children }) {
+ // Add a name to animate with a shared element transition.
+ // This uses the default animation, no additional css needed.
+ return (
+
+
+ {/* Custom classes based on transition type. */}
+
+ {heading}
+
+ {isPending && }
+
+
+ {/* Opt-out of ViewTransition for the content. */}
+ {/* Content can define it's own ViewTransition. */}
+
+
+
{children}
+
+
+
+ );
+}
+```
+
+```js src/LikeButton.js hidden
+import {useState} from 'react';
+import {Heart} from './Icons';
+
+// A hack since we don't actually have a backend.
+// Unlike local state, this survives videos being filtered.
+const likedVideos = new Set();
+
+export default function LikeButton({video}) {
+ const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
+ const [animate, setAnimate] = useState(false);
+ return (
+
+ );
+}
+```
+
+```js src/Videos.js hidden
+import { useState, unstable_ViewTransition as ViewTransition } from "react";
+import LikeButton from "./LikeButton";
+import { useRouter } from "./router";
+import { PauseIcon, PlayIcon } from "./Icons";
+import { startTransition } from "react";
+
+export function Thumbnail({ video, children }) {
+ // Add a name to animate with a shared element transition.
+ // This uses the default animation, no additional css needed.
+ return (
+
+
+ {/* Custom classes based on transition type. */}
+
+ {heading}
+
+ {isPending && }
+
+
+ {/* Opt-out of ViewTransition for the content. */}
+ {/* Content can define it's own ViewTransition. */}
+
+
+
{children}
+
+
+
+ );
+}
+```
+
+```js src/LikeButton.js hidden
+import {useState} from 'react';
+import {Heart} from './Icons';
+
+// A hack since we don't actually have a backend.
+// Unlike local state, this survives videos being filtered.
+const likedVideos = new Set();
+
+export default function LikeButton({video}) {
+ const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
+ const [animate, setAnimate] = useState(false);
+ return (
+
+ );
+}
+```
+
+```js src/Videos.js hidden
+import { useState, unstable_ViewTransition as ViewTransition } from "react";
+import LikeButton from "./LikeButton";
+import { useRouter } from "./router";
+import { PauseIcon, PlayIcon } from "./Icons";
+import { startTransition } from "react";
+
+export function Thumbnail({ video, children }) {
+ // Add a name to animate with a shared element transition.
+ // This uses the default animation, no additional css needed.
+ return (
+
+
+ {/* Custom classes based on transition type. */}
+
+ {heading}
+
+ {isPending && }
+
+
+ {/* Opt-out of ViewTransition for the content. */}
+ {/* Content can define it's own ViewTransition. */}
+
+
+
{children}
+
+
+
+ );
+}
+```
+
+```js src/LikeButton.js hidden
+import {useState} from 'react';
+import {Heart} from './Icons';
+
+// A hack since we don't actually have a backend.
+// Unlike local state, this survives videos being filtered.
+const likedVideos = new Set();
+
+export default function LikeButton({video}) {
+ const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
+ const [animate, setAnimate] = useState(false);
+ return (
+
+ );
+}
+```
+
+```js src/Videos.js
+import { useState, unstable_ViewTransition as ViewTransition } from "react"; import LikeButton from "./LikeButton"; import { useRouter } from "./router"; import { PauseIcon, PlayIcon } from "./Icons"; import { startTransition } from "react";
+
+export function Thumbnail({ video, children }) {
+ // Add a name to animate with a shared element transition.
+ return (
+
+
+ {/* Custom classes based on transition type. */}
+
+ {heading}
+
+ {isPending && }
+
+
+ {/* Opt-out of ViewTransition for the content. */}
+ {/* Content can define it's own ViewTransition. */}
+
+
+
{children}
+
+
+
+ );
+}
+```
+
+```js src/LikeButton.js hidden
+import {useState} from 'react';
+import {Heart} from './Icons';
+
+// A hack since we don't actually have a backend.
+// Unlike local state, this survives videos being filtered.
+const likedVideos = new Set();
+
+export default function LikeButton({video}) {
+ const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
+ const [animate, setAnimate] = useState(false);
+ return (
+
+ );
+}
+```
+
+```js src/Videos.js hidden
+import { useState, unstable_ViewTransition as ViewTransition } from "react";
+import LikeButton from "./LikeButton";
+import { useRouter } from "./router";
+import { PauseIcon, PlayIcon } from "./Icons";
+import { startTransition } from "react";
+
+export function Thumbnail({ video, children }) {
+ // Add a name to animate with a shared element transition.
+ // This uses the default animation, no additional css needed.
+ return (
+
+
+ {/* Custom classes based on transition type. */}
+
+ {heading}
+
+ {isPending && }
+
+
+ {/* Opt-out of ViewTransition for the content. */}
+ {/* Content can define it's own ViewTransition. */}
+
+
+
{children}
+
+
+
+ );
+}
+```
+
+```js src/LikeButton.js hidden
+import {useState} from 'react';
+import {Heart} from './Icons';
+
+// A hack since we don't actually have a backend.
+// Unlike local state, this survives videos being filtered.
+const likedVideos = new Set();
+
+export default function LikeButton({video}) {
+ const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));
+ const [animate, setAnimate] = useState(false);
+ return (
+
+ );
+}
+```
+
+```js src/Videos.js hidden
+import { useState, unstable_ViewTransition as ViewTransition } from "react";
+import LikeButton from "./LikeButton";
+import { useRouter } from "./router";
+import { PauseIcon, PlayIcon } from "./Icons";
+import { startTransition } from "react";
+
+export function Thumbnail({ video, children }) {
+ // Add a name to animate with a shared element transition.
+ // This uses the default animation, no additional css needed.
+ return (
+
+
+ );
+}
+```
+
+
+```js src/data.js hidden
+const videos = [
+ {
+ id: '1',
+ title: 'First video',
+ description: 'Video description',
+ image: 'blue',
+ },
+ {
+ id: '2',
+ title: 'Second video',
+ description: 'Video description',
+ image: 'red',
+ },
+ {
+ id: '3',
+ title: 'Third video',
+ description: 'Video description',
+ image: 'green',
+ },
+ {
+ id: '4',
+ title: 'Fourth video',
+ description: 'Video description',
+ image: 'purple',
+ },
+ {
+ id: '5',
+ title: 'Fifth video',
+ description: 'Video description',
+ image: 'yellow',
+ },
+ {
+ id: '6',
+ title: 'Sixth video',
+ description: 'Video description',
+ image: 'gray',
+ },
+];
+
+let videosCache = new Map();
+let videoCache = new Map();
+let videoDetailsCache = new Map();
+const VIDEO_DELAY = 1;
+const VIDEO_DETAILS_DELAY = 1000;
+export function fetchVideos() {
+ if (videosCache.has(0)) {
+ return videosCache.get(0);
+ }
+ const promise = new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(videos);
+ }, VIDEO_DELAY);
+ });
+ videosCache.set(0, promise);
+ return promise;
+}
+
+export function fetchVideo(id) {
+ if (videoCache.has(id)) {
+ return videoCache.get(id);
+ }
+ const promise = new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(videos.find((video) => video.id === id));
+ }, VIDEO_DELAY);
+ });
+ videoCache.set(id, promise);
+ return promise;
+}
+
+export function fetchVideoDetails(id) {
+ if (videoDetailsCache.has(id)) {
+ return videoDetailsCache.get(id);
+ }
+ const promise = new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(videos.find((video) => video.id === id));
+ }, VIDEO_DETAILS_DELAY);
+ });
+ videoDetailsCache.set(id, promise);
+ return promise;
+}
+```
+
+```js src/router.js hidden
+import {useState, createContext, use, useTransition, useLayoutEffect, useEffect, unstable_addTransitionType as addTransitionType} from "react";
+
+export function Router({ children }) {
+ const [isPending, startTransition] = useTransition();
+ const [routerState, setRouterState] = useState({pendingNav: () => {}, url: document.location.pathname});
+ function navigate(url) {
+ startTransition(() => {
+ // Transition type for the cause "nav forward"
+ addTransitionType('nav-forward');
+ go(url);
+ });
+ }
+ function navigateBack(url) {
+ startTransition(() => {
+ // Transition type for the cause "nav backward"
+ addTransitionType('nav-back');
+ go(url);
+ });
+ }
+
+ function go(url) {
+ setRouterState({
+ url,
+ pendingNav() {
+ window.history.pushState({}, "", url);
+ },
+ });
+ }
+
+ useEffect(() => {
+ function handlePopState() {
+ // This should not animate because restoration has to be synchronous.
+ // Even though it's a transition.
+ startTransition(() => {
+ setRouterState({
+ url: document.location.pathname + document.location.search,
+ pendingNav() {
+ // Noop. URL has already updated.
+ },
+ });
+ });
+ }
+ window.addEventListener("popstate", handlePopState);
+ return () => {
+ window.removeEventListener("popstate", handlePopState);
+ };
+ }, []);
+ const pendingNav = routerState.pendingNav;
+ useLayoutEffect(() => {
+ pendingNav();
+ }, [pendingNav]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+const RouterContext = createContext({ url: "/", params: {} });
+
+export function useRouter() {
+ return use(RouterContext);
+}
+
+export function useIsNavPending() {
+ return use(RouterContext).isPending;
+}
+
+```
+
+```css src/styles.css hidden
+@font-face {
+ font-family: Optimistic Text;
+ src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format("woff2");
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Optimistic Text;
+ src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format("woff2");
+ font-weight: 500;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Optimistic Text;
+ src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format("woff2");
+ font-weight: 600;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Optimistic Text;
+ src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format("woff2");
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html {
+ background-image: url(https://react.dev/images/meta-gradient-dark.png);
+ background-size: 100%;
+ background-position: -100%;
+ background-color: rgb(64 71 86);
+ background-repeat: no-repeat;
+ height: 100%;
+ width: 100%;
+}
+
+body {
+ font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
+ padding: 10px 0 10px 0;
+ margin: 0;
+ display: flex;
+ justify-content: center;
+}
+
+#root {
+ flex: 1 1;
+ height: auto;
+ background-color: #fff;
+ border-radius: 10px;
+ max-width: 450px;
+ min-height: 600px;
+ padding-bottom: 10px;
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 22px;
+}
+
+h2 {
+ margin-top: 0;
+ font-size: 20px;
+}
+
+h3 {
+ margin-top: 0;
+ font-size: 18px;
+}
+
+h4 {
+ margin-top: 0;
+ font-size: 16px;
+}
+
+h5 {
+ margin-top: 0;
+ font-size: 14px;
+}
+
+h6 {
+ margin-top: 0;
+ font-size: 12px;
+}
+
+code {
+ font-size: 1.2em;
+}
+
+ul {
+ padding-inline-start: 20px;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
+.absolute {
+ position: absolute;
+}
+
+.overflow-visible {
+ overflow: visible;
+}
+
+.visible {
+ overflow: visible;
+}
+
+.fit {
+ width: fit-content;
+}
+
+
+/* Layout */
+.page {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.top-hero {
+ height: 200px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-image: conic-gradient(
+ from 90deg at -10% 100%,
+ #2b303b 0deg,
+ #2b303b 90deg,
+ #16181d 1turn
+ );
+}
+
+.bottom {
+ flex: 1;
+ overflow: auto;
+}
+
+.top-nav {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 0;
+ padding: 0 12px;
+ top: 0;
+ width: 100%;
+ height: 44px;
+ color: #23272f;
+ font-weight: 700;
+ font-size: 20px;
+ z-index: 100;
+ cursor: default;
+}
+
+.content {
+ padding: 0 12px;
+ margin-top: 4px;
+}
+
+
+.loader {
+ color: #23272f;
+ font-size: 3px;
+ width: 1em;
+ margin-right: 18px;
+ height: 1em;
+ border-radius: 50%;
+ position: relative;
+ text-indent: -9999em;
+ animation: loading-spinner 1.3s infinite linear;
+ animation-delay: 200ms;
+ transform: translateZ(0);
+}
+
+@keyframes loading-spinner {
+ 0%,
+ 100% {
+ box-shadow: 0 -3em 0 0.2em,
+ 2em -2em 0 0em, 3em 0 0 -1em,
+ 2em 2em 0 -1em, 0 3em 0 -1em,
+ -2em 2em 0 -1em, -3em 0 0 -1em,
+ -2em -2em 0 0;
+ }
+ 12.5% {
+ box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,
+ 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,
+ -2em 2em 0 -1em, -3em 0 0 -1em,
+ -2em -2em 0 -1em;
+ }
+ 25% {
+ box-shadow: 0 -3em 0 -0.5em,
+ 2em -2em 0 0, 3em 0 0 0.2em,
+ 2em 2em 0 0, 0 3em 0 -1em,
+ -2em 2em 0 -1em, -3em 0 0 -1em,
+ -2em -2em 0 -1em;
+ }
+ 37.5% {
+ box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,
+ 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,
+ -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;
+ }
+ 50% {
+ box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,
+ 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,
+ -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;
+ }
+ 62.5% {
+ box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,
+ 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,
+ -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;
+ }
+ 75% {
+ box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,
+ 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,
+ -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;
+ }
+ 87.5% {
+ box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,
+ 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,
+ -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;
+ }
+}
+
+/* LikeButton */
+.like-button {
+ outline-offset: 2px;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.5rem;
+ height: 2.5rem;
+ cursor: pointer;
+ border-radius: 9999px;
+ border: none;
+ outline: none 2px;
+ color: #5e687e;
+ background: none;
+}
+
+.like-button:focus {
+ color: #a6423a;
+ background-color: rgba(166, 66, 58, .05);
+}
+
+.like-button:active {
+ color: #a6423a;
+ background-color: rgba(166, 66, 58, .05);
+ transform: scaleX(0.95) scaleY(0.95);
+}
+
+.like-button:hover {
+ background-color: #f6f7f9;
+}
+
+.like-button.liked {
+ color: #a6423a;
+}
+
+/* Icons */
+@keyframes circle {
+ 0% {
+ transform: scale(0);
+ stroke-width: 16px;
+ }
+
+ 50% {
+ transform: scale(.5);
+ stroke-width: 16px;
+ }
+
+ to {
+ transform: scale(1);
+ stroke-width: 0;
+ }
+}
+
+.circle {
+ color: rgba(166, 66, 58, .5);
+ transform-origin: center;
+ transition-property: all;
+ transition-duration: .15s;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+}
+
+.circle.liked.animate {
+ animation: circle .3s forwards;
+}
+
+.heart {
+ width: 1.5rem;
+ height: 1.5rem;
+}
+
+.heart.liked {
+ transform-origin: center;
+ transition-property: all;
+ transition-duration: .15s;
+ transition-timing-function: cubic-bezier(.4, 0, .2, 1);
+}
+
+.heart.liked.animate {
+ animation: scale .35s ease-in-out forwards;
+}
+
+.control-icon {
+ color: hsla(0, 0%, 100%, .5);
+ filter: drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));
+}
+
+.chevron-left {
+ margin-top: 2px;
+ rotate: 90deg;
+}
+
+
+/* Video */
+.thumbnail {
+ position: relative;
+ aspect-ratio: 16 / 9;
+ display: flex;
+ overflow: hidden;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ border-radius: 0.5rem;
+ outline-offset: 2px;
+ width: 8rem;
+ vertical-align: middle;
+ background-color: #ffffff;
+ background-size: cover;
+ user-select: none;
+}
+
+.thumbnail.blue {
+ background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);
+}
+
+.thumbnail.red {
+ background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);
+}
+
+.thumbnail.green {
+ background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);
+}
+
+.thumbnail.purple {
+ background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);
+}
+
+.thumbnail.yellow {
+ background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);
+}
+
+.thumbnail.gray {
+ background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);
+}
+
+.video {
+ display: flex;
+ flex-direction: row;
+ gap: 0.75rem;
+ align-items: center;
+}
+
+.video .link {
+ display: flex;
+ flex-direction: row;
+ flex: 1 1 0;
+ gap: 0.125rem;
+ outline-offset: 4px;
+ cursor: pointer;
+}
+
+.video .info {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ margin-left: 8px;
+ gap: 0.125rem;
+}
+
+.video .info:hover {
+ text-decoration: underline;
+}
+
+.video-title {
+ font-size: 15px;
+ line-height: 1.25;
+ font-weight: 700;
+ color: #23272f;
+}
+
+.video-description {
+ color: #5e687e;
+ font-size: 13px;
+}
+
+/* Details */
+.details .thumbnail {
+ position: relative;
+ aspect-ratio: 16 / 9;
+ display: flex;
+ overflow: hidden;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ border-radius: 0.5rem;
+ outline-offset: 2px;
+ width: 100%;
+ vertical-align: middle;
+ background-color: #ffffff;
+ background-size: cover;
+ user-select: none;
+}
+
+.video-details-title {
+ margin-top: 8px;
+}
+
+.video-details-speaker {
+ display: flex;
+ gap: 8px;
+ margin-top: 10px
+}
+
+.back {
+ display: flex;
+ align-items: center;
+ margin-left: -5px;
+ cursor: pointer;
+}
+
+.back:hover {
+ text-decoration: underline;
+}
+
+.info-title {
+ font-size: 1.5rem;
+ font-weight: 700;
+ line-height: 1.25;
+ margin: 8px 0 0 0 ;
+}
+
+.info-description {
+ margin: 8px 0 0 0;
+}
+
+.controls {
+ cursor: pointer;
+}
+
+.fallback {
+ background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;
+ background-size: 800px 104px;
+ display: block;
+ line-height: 1.25;
+ margin: 8px 0 0 0;
+ border-radius: 5px;
+ overflow: hidden;
+
+ animation: 1s linear 1s infinite shimmer;
+ animation-delay: 300ms;
+ animation-duration: 1s;
+ animation-fill-mode: forwards;
+ animation-iteration-count: infinite;
+ animation-name: shimmer;
+ animation-timing-function: linear;
+}
+
+
+.fallback.title {
+ width: 130px;
+ height: 30px;
+
+}
+
+.fallback.description {
+ width: 150px;
+ height: 21px;
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: -468px 0;
+ }
+
+ 100% {
+ background-position: 468px 0;
+ }
+}
+
+.search {
+ margin-bottom: 10px;
+}
+.search-input {
+ width: 100%;
+ position: relative;
+}
+
+.search-icon {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ inset-inline-start: 0;
+ display: flex;
+ align-items: center;
+ padding-inline-start: 1rem;
+ pointer-events: none;
+ color: #99a1b3;
+}
+
+.search-input input {
+ display: flex;
+ padding-inline-start: 2.75rem;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ width: 100%;
+ text-align: start;
+ background-color: rgb(235 236 240);
+ outline: 2px solid transparent;
+ cursor: pointer;
+ border: none;
+ align-items: center;
+ color: rgb(35 39 47);
+ border-radius: 9999px;
+ vertical-align: middle;
+ font-size: 15px;
+}
+
+.search-input input:hover, .search-input input:active {
+ background-color: rgb(235 236 240/ 0.8);
+ color: rgb(35 39 47/ 0.8);
+}
+
+/* Home */
+.video-list {
+ position: relative;
+}
+
+.video-list .videos {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ overflow-y: auto;
+ height: 100%;
+}
+```
+
+
+```css src/animations.css
+/* No additional animations needed */
+
+
+
+
+
+
+
+
+
+/* Previously defined animations below */
+
+
+
+
+
+
+/* Slide animations for Suspense the fallback down */
+::view-transition-old(.slide-down) {
+ animation: 150ms ease-out both fade-out, 150ms ease-out both slide-down;
+}
+
+::view-transition-new(.slide-up) {
+ animation: 210ms ease-in 150ms both fade-in, 400ms ease-in both slide-up;
+}
+
+/* Animations for view transition classed added by transition type */
+::view-transition-old(.slide-forward) {
+ /* when sliding forward, the "old" page should slide out to left. */
+ animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
+ 400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
+}
+
+::view-transition-new(.slide-forward) {
+ /* when sliding forward, the "new" page should slide in from right. */
+ animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,
+ 400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
+}
+
+::view-transition-old(.slide-back) {
+ /* when sliding back, the "old" page should slide out to right. */
+ animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
+ 400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
+}
+
+::view-transition-new(.slide-back) {
+ /* when sliding back, the "new" page should slide in from left. */
+ animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,
+ 400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;
+}
+
+/* Keyframes to support our animations above. */
+@keyframes slide-up {
+ from {
+ transform: translateY(10px);
+ }
+ to {
+ transform: translateY(0);
+ }
+}
+
+@keyframes slide-down {
+ from {
+ transform: translateY(0);
+ }
+ to {
+ transform: translateY(10px);
+ }
+}
+
+@keyframes fade-in {
+ from {
+ opacity: 0;
+ }
+}
+
+@keyframes fade-out {
+ to {
+ opacity: 0;
+ }
+}
+
+@keyframes slide-to-right {
+ to {
+ transform: translateX(50px);
+ }
+}
+
+@keyframes slide-from-right {
+ from {
+ transform: translateX(50px);
+ }
+ to {
+ transform: translateX(0);
+ }
+}
+
+@keyframes slide-to-left {
+ to {
+ transform: translateX(-50px);
+ }
+}
+
+@keyframes slide-from-left {
+ from {
+ transform: translateX(-50px);
+ }
+ to {
+ transform: translateX(0);
+ }
+}
+
+/* Default .slow-fade. */
+::view-transition-old(.slow-fade) {
+ animation-duration: 500ms;
+}
+
+::view-transition-new(.slow-fade) {
+ animation-duration: 500ms;
+}
+```
+
+```js src/index.js hidden
+import React, {StrictMode} from 'react';
+import {createRoot} from 'react-dom/client';
+import './styles.css';
+import './animations.css';
+
+import App from './App';
+import {Router} from './router';
+
+const root = createRoot(document.getElementById('root'));
+root.render(
+
+
+
+
+
+);
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "experimental",
+ "react-dom": "experimental",
+ "react-scripts": "latest"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
+```
+
+
+
+### Server-Side Rendering with Activity {/*server-side-rendering-with-activity*/}
+
+When using Activity on a page that uses server-side rendering (SSR), there are additional optimizations.
+
+If part of the page is rendered with `mode="hidden"`, then it will not be included in the SSR response. Instead, React will schedule a client render for the content inside Activity while the rest of the page hydrates, prioritizing the visible content on screen.
+
+For parts of the UI rendered with `mode="visible"`, React will de-prioritize hydration of content within Activity, similar to how Suspense content is hydrated at a lower priority. If the user interacts with the page, we'll prioritize hydration within the boundary if needed.
+
+These are advanced use cases, but they show the additional benefits considered with Activity.
+
+### Future modes for Activity {/*future-modes-for-activity*/}
+
+In the future, we may add more modes to Activity.
+
+For example, a common use case is rendering a modal, where the previous "inactive" page is visible behind the "active" modal view. The "hidden" mode does not work for this use case because it's not visible and not included in SSR.
+
+Instead, we're considering a new mode that would keep the content visible—and included in SSR—but keep it unmounted and de-prioritize updates. This mode may also need to "pause" DOM updates, since it can be distracting to see backgrounded content updating while a modal is open.
+
+Another mode we're considering for Activity is the ability to automatically destroy state for hidden Activities if there is too much memory being used. Since the component is already unmounted, it may be preferable to destroy state for the least recently used hidden parts of the app rather than consume too many resources.
+
+These are areas we're still exploring, and we'll share more as we make progress. For more information on what Activity includes today, [check out the docs](/reference/react/Activity).
+
+---
+
+# Features in development {/*features-in-development*/}
+
+We're also developing features to help solve the common problems below.
+
+As we iterate on possible solutions, you may see some potential APIs we're testing being shared based on the PRs we are landing. Please keep in mind that as we try different ideas, we often change or remove different solutions after trying them out.
+
+When the solutions we're working on are shared too early, it can create churn and confusion in the community. To balance being transparent and limiting confusion, we're sharing the problems we're currently developing solutions for, without sharing a particular solution we have in mind.
+
+As these features progress, we'll announce them on the blog with docs included so you can try them out.
+
+## React Performance Tracks {/*react-performance-tracks*/}
+
+We're working on a new set of custom tracks to performance profilers using browser APIs that [allow adding custom tracks](https://developer.chrome.com/docs/devtools/performance/extension) to provide more information about the performance of your React app.
+
+This feature is still in progress, so we're not ready to publish docs to fully release it as an experimental feature yet. You can get a sneak preview when using an experimental version of React, which will automatically add the performance tracks to profiles:
+
+
+
+
+
+
+
+
+
+
+
+
+There are a few known issues we plan to address such as performance, and the scheduler track not always "connecting" work across Suspended trees, so it's not quite ready to try. We're also still collecting feedback from early adopters to improve the design and usability of the tracks.
+
+Once we solve those issues, we'll publish experimental docs and share that it's ready to try.
+
+---
+
+## Automatic Effect Dependencies {/*automatic-effect-dependencies*/}
+
+When we released hooks, we had three motivations:
+
+- **Sharing code between components**: hooks replaced patterns like render props and higher-order components to allow you to reuse stateful logic without changing your component hierarchy.
+- **Think in terms of function, not lifecycles**: hooks let you split one component into smaller functions based on what pieces are related (such as setting up a subscription or fetching data), rather than forcing a split based on lifecycle methods.
+- **Support ahead-of-time compilation**: hooks were designed to support ahead-of-time compilation with less pitfalls causing unintentional de-optimizations caused by lifecycle methods, and limitations of classes.
+
+Since their release, hooks have been successful at *sharing code between components*. Hooks are now the favored way to share logic between components, and there are less use cases for render props and higher order components. Hooks have also been successful at supporting features like Fast Refresh that were not possible with class components.
+
+### Effects can be hard {/*effects-can-be-hard*/}
+
+Unfortunately, some hooks are still hard to think in terms of function instead of lifecycles. Effects specifically are still hard to understand and are the most common pain point we hear from developers. Last year, we spent a significant amount of time researching how Effects were used, and how those use cases could be simplified and easier to understand.
+
+We found that often, the confusion is from using an Effect when you don't need to. The [You Might Not Need an Effect](/learn/you-might-not-need-an-effect) guide covers many cases for when Effects are not the right solution. However, even when an Effect is the right fit for a problem, Effects can still be harder to understand than class component lifecycles.
+
+We believe one of the reasons for confusion is that developers to think of Effects from the _component's_ perspective (like a lifecycle), instead of the _Effects_ point of view (what the Effect does).
+
+Let's look at an example [from the docs](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective):
+
+```js
+useEffect(() => {
+ // Your Effect connected to the room specified with roomId...
+ const connection = createConnection(serverUrl, roomId);
+ connection.connect();
+ return () => {
+ // ...until it disconnected
+ connection.disconnect();
+ };
+}, [roomId]);
+```
+
+Many users would read this code as "on mount, connect to the roomId. whenever `roomId` changes, disconnect to the old room and re-create the connection". However, this is thinking from the component's lifecycle perspective, which means you will need to think of every component lifecycle state to write the Effect correctly. This can be difficult, so it's understandable that Effects seem harder than class lifecycles when using the component perspective.
+
+### Effects without dependencies {/*effects-without-dependencies*/}
+
+Instead, it's better to think from the Effect's perspective. The Effect doesn't know about the component lifecycles. It only describes how to start synchronization and how to stop it. When users think of Effects in this way, their Effects tend to be easier to write, and more resilient to being started and stopped as many times as is needed.
+
+We spent some time researching why Effects are thought of from the component perspective, and we think one of the reasons is the dependency array. Since you have to write it, it's right there and in your face reminding you of what you're "reacting" to and baiting you into the mental model of 'do this when these values change'.
+
+When we released hooks, we knew we could make them easier to use with ahead-of-time compilation. With the React Compiler, you're now able to avoid writing `useCallback` and `useMemo` yourself in most cases. For Effects, the compiler can insert the dependencies for you:
+
+```js
+useEffect(() => {
+ const connection = createConnection(serverUrl, roomId);
+ connection.connect();
+ return () => {
+ connection.disconnect();
+ };
+}); // compiler inserted dependencies.
+```
+
+With this code, the React Compiler can infer the dependencies for you and insert them automatically so you don't need to see or write them. With features like [the IDE extension](#compiler-ide-extension) and [`useEffectEvent`](/reference/react/experimental_useEffectEvent), we can provide a CodeLens to show you what the Compiler inserted for times you need to debug, or to optimize by removing a dependency. This helps reinforce the correct mental model for writing Effects, which can run at any time to synchronize your component or hook's state with something else.
+
+Our hope is that automatically inserting dependencies is not only easier to write, but that it also makes them easier to understand by forcing you to think in terms of what the Effect does, and not in component lifecycles.
+
+---
+
+## Compiler IDE Extension {/*compiler-ide-extension*/}
+
+Earlier this week [we shared](/blog/2025/04/21/react-compiler-rc) the React Compiler release candidate, and we're working towards shipping the first SemVer stable version of the compiler in the coming months.
+
+We've also begun exploring ways to use the React Compiler to provide information that can improve understanding and debugging your code. One idea we've started exploring is a new experimental LSP-based React IDE extension powered by React Compiler, similar to the extension used in [Lauren Tan's React Conf talk](https://conf2024.react.dev/talks/5).
+
+Our idea is that we can use the compiler's static analysis to provide more information, suggestions, and optimization opportunities directly in your IDE. For example, we can provide diagnostics for code breaking the Rules of React, hovers to show if components and hooks were optimized by the compiler, or a CodeLens to see [automatically inserted Effect dependencies](#automatic-effect-dependencies).
+
+The IDE extension is still an early exploration, but we'll share our progress in future updates.
+
+---
+
+## Fragment Refs {/*fragment-refs*/}
+
+Many DOM APIs like those for event management, positioning, and focus are difficult to compose when writing with React. This often leads developers to reach for Effects, managing multiple Refs, by using APIs like `findDOMNode` (removed in React 19).
+
+We are exploring adding refs to Fragments that would point to a group of DOM elements, rather than just a single element. Our hope is that this will simplify managing multiple children and make it easier to write composable React code when calling DOM APIs.
+
+Fragment refs are still being researched. We'll share more when we're closer to having the final API finished.
+
+---
+
+## Gesture Animations {/*gesture-animations*/}
+
+We're also researching ways to enhance View Transitions to support gesture animations such as swiping to open a menu, or scroll through a photo carousel.
+
+Gestures present new challenges for a few reasons:
+
+- **Gestures are continuous**: as you swipe the animation is tied to your finger placement time, rather than triggering and running to completion.
+- **Gestures don't complete**: when you release your finger gesture animations can run to completion, or revert to their original state (like when you only partially open a menu) depending on how far you go.
+- **Gestures invert old and new**: while you're animating, you want the page you are animating from to stay "alive" and interactive. This inverts the browser View Transition model where the "old" state is a snapshot and the "new" state is the live DOM.
+
+We believe we’ve found an approach that works well and may introduce a new API for triggering gesture transitions. For now, we're focused on shipping ``, and will revisit gestures afterward.
+
+---
+
+## Concurrent Stores {/*concurrent-stores*/}
+
+When we released React 18 with concurrent rendering, we also released `useSyncExternalStore` so external store libraries that did not use React state or context could [support concurrent rendering](https://github.com/reactwg/react-18/discussions/70) by forcing a synchronous render when the store is updated.
+
+Using `useSyncExternalStore` comes at a cost though, since it forces a bail out from concurrent features like transitions, and forces existing content to show Suspense fallbacks.
+
+Now that React 19 has shipped, we're revisiting this problem space to create a primitive to fully support concurrent external stores with the `use` API:
+
+```js
+const value = use(store);
+```
+
+Our goal is to allow external state to be read during render without tearing, and to work seamlessly with all of the concurrent features React offers.
+
+This research is still early. We'll share more, and what the new APIs will look like, when we're further along.
+
+---
+
+_Thanks to [Aurora Scharff](https://bsky.app/profile/aurorascharff.no), [Dan Abramov](https://bsky.app/profile/danabra.mov), [Eli White](https://twitter.com/Eli_White), [Lauren Tan](https://bsky.app/profile/no.lol), [Luna Wei](https://github.com/lunaleaps), [Matt Carroll](https://twitter.com/mattcarrollcode), [Jack Pope](https://jackpope.me), [Jason Bonta](https://threads.net/someextent), [Jordan Brown](https://github.com/jbrown215), [Jordan Eldredge](https://bsky.app/profile/capt.dev), [Mofei Zhang](https://threads.net/z_mofei), [Sebastien Lorber](https://bsky.app/profile/sebastienlorber.com), [Sebastian Markbåge](https://bsky.app/profile/sebmarkbage.calyptus.eu), and [Tim Yung](https://github.com/yungsters) for reviewing this post._
diff --git a/src/content/blog/index.md b/src/content/blog/index.md
index f6d0ded75..1bc31b5d1 100644
--- a/src/content/blog/index.md
+++ b/src/content/blog/index.md
@@ -4,19 +4,31 @@ title: React 블로그
-이 블로그는 React 팀의 업데이트에 대한 공식 출처입니다. 릴리스 노트 및 더 이상 사용되지 않는 기능들에 대한 공지Deprecation Notice를 비롯한 중요 내용들이 이곳에 먼저 공유됩니다. 트위터에서 [@reactjs](https://twitter.com/reactjs) 계정을 팔로우해도 좋지만, 이 블로그만으로도 모든 정보를 얻을 수 있습니다.
+이 블로그는 React 팀의 업데이트에 대한 공식 출처입니다. 릴리스 노트 및 더 이상 사용되지 않는 기능들에 대한 공지Deprecation Notice를 비롯한 중요 내용들을 이곳에 먼저 공유합니다. 블루스카이의 [@react.dev](https://bsky.app/profile/react.dev) 혹은 트위터의 [@reactjs](https://twitter.com/reactjs) 계정을 팔로우해도 좋지만, 이 블로그만으로도 모든 정보를 얻을 수 있습니다.
-
+
+
+In React Labs posts, we write about projects in active research and development. In this post, we're sharing two new experimental features that are ready to try today, and sharing other areas we're working on now ...
+
+
+
+
+
+We are releasing the compiler's first Release Candidate (RC) today.
+
+
+
+
새로운 앱에 대한 Create React App 사용을 중단하며, 기존 앱은 프레임워크나 Vite, Parcel, RSBuild 같은 빌드 도구로의 마이그레이션을 권장합니다. 또한 프레임워크가 프로젝트와 맞지 않거나, 자신만의 프레임워크를 구축하고 싶거나, 혹은 React가 어떻게 작동하는지 배우기 위해 React 앱을 처음부터 만들어 보고 싶은 사용자들을 위한 문서를 제공합니다.
-
+
React 19 업그레이드 가이드에서 React 19로 앱을 업그레이드하는 단계별 지침을 공유했습니다. 이 포스트에서 React 19의 새로운 기능들과 이를 도입하는 방법을 제공합니다.
diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md
index 389049ead..e83ec21f1 100644
--- a/src/content/community/conferences.md
+++ b/src/content/community/conferences.md
@@ -26,6 +26,11 @@ 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)
@@ -51,6 +56,16 @@ 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)
+### React Summit US 2025 {/*react-summit-us-2025*/}
+November 18 - 21, 2025. In-person in New York, USA + remote (hybrid event)
+
+[Website](https://reactsummit.us/) - [Twitter](https://x.com/reactsummit)
+
+### React Advanced London 2025 {/*react-advanced-london-2025*/}
+November 28 & December 1, 2025. In-person in London, UK + online (hybrid event)
+
+[Website](https://reactadvanced.com/) - [Twitter](https://x.com/reactadvanced)
+
## Past Conferences {/*past-conferences*/}
diff --git a/src/content/community/index.md b/src/content/community/index.md
index 7874616ef..a4fadafbe 100644
--- a/src/content/community/index.md
+++ b/src/content/community/index.md
@@ -29,4 +29,4 @@ React의 베스트 프랙티스, 애플리케이션 아키텍처, 그리고 Reac
## 뉴스 {/*news*/}
-React에 대한 최신 뉴스는 [Twitter의 **@reactjs**](https://twitter.com/reactjs)와 이 웹사이트의 [공식 React 블로그](/blog/)에서 확인하세요.
+React에 대한 최신 뉴스는 [Twitter의 **@reactjs**](https://twitter.com/reactjs), [Bluesky의 **@react.dev**](https://bsky.app/profile/react.dev), 이 웹사이트의 [공식 React 블로그](/blog/)에서 확인하세요.
diff --git a/src/content/community/meetups.md b/src/content/community/meetups.md
index 186740341..81fc5c210 100644
--- a/src/content/community/meetups.md
+++ b/src/content/community/meetups.md
@@ -38,7 +38,7 @@ Do you have a local React.js meetup? Add it here! (Please keep the list alphabet
## Canada {/*canada*/}
* [Halifax, NS](https://www.meetup.com/Halifax-ReactJS-Meetup/)
-* [Montreal, QC - React Native](https://www.meetup.com/fr-FR/React-Native-MTL/)
+* [Montreal, QC](https://guild.host/react-montreal/)
* [Vancouver, BC](https://www.meetup.com/ReactJS-Vancouver-Meetup/)
* [Ottawa, ON](https://www.meetup.com/Ottawa-ReactJS-Meetup/)
* [Saskatoon, SK](https://www.meetup.com/saskatoon-react-meetup/)
diff --git a/src/content/learn/build-a-react-app-from-scratch.md b/src/content/learn/build-a-react-app-from-scratch.md
index f14b5f109..93e2a0c7a 100644
--- a/src/content/learn/build-a-react-app-from-scratch.md
+++ b/src/content/learn/build-a-react-app-from-scratch.md
@@ -116,7 +116,7 @@ Similarly, if you rely on the apps using your framework to split the code, you m
Splitting code by route, when integrated with bundling and data fetching, can reduce the initial load time of your app and the time it takes for the largest visible content of the app to render ([Largest Contentful Paint](https://web.dev/articles/lcp)).
For code-splitting instructions, see your build tool docs:
-- [Vite build optimizations](https://v3.vitejs.dev/guide/features.html#build-optimizations)
+- [Vite build optimizations](https://vite.dev/guide/features.html#build-optimizations)
- [Parcel code splitting](https://parceljs.org/features/code-splitting/)
- [Rsbuild code splitting](https://rsbuild.dev/guide/optimization/code-splitting)
diff --git a/src/content/learn/creating-a-react-app.md b/src/content/learn/creating-a-react-app.md
index 5a482167a..2bc057c49 100644
--- a/src/content/learn/creating-a-react-app.md
+++ b/src/content/learn/creating-a-react-app.md
@@ -32,7 +32,7 @@ React로 새로운 앱이나 웹사이트를 구축하려면 프레임워크부
npx create-next-app@latest
-Next.js는 [Vercel](https://vercel.com/)에서 유지 관리합니다. [Next.js 앱을 빌드](https://nextjs.org/docs/app/building-your-application/deploying)해서 Node.js와 서버리스 호스팅 혹은 자체 서버에 배포할 수 있습니다. Next.js는 또한 서버가 필요없는 [정적 내보내기](https://nextjs.org/docs/app/building-your-application/deploying/static-exports)도 지원합니다. Vercel은 추가적으로 옵트인 유료 클라우드 서비스도 지원합니다.
+Next.js는 [Vercel](https://vercel.com/)에서 유지 관리합니다. [Next.js 앱을 빌드](https://nextjs.org/docs/app/building-your-application/deploying)해서 Node.js, 도커 컨테이너, 서버리스 호스팅, 혹은 자체 서버에 배포할 수 있습니다. Next.js는 또한 서버가 필요없는 [정적 내보내기](https://nextjs.org/docs/app/building-your-application/deploying/static-exports)도 지원합니다.
### React Router (v7) {/*react-router-v7*/}
diff --git a/src/content/learn/editor-setup.md b/src/content/learn/editor-setup.md
index 0d55479bf..022a05325 100644
--- a/src/content/learn/editor-setup.md
+++ b/src/content/learn/editor-setup.md
@@ -4,41 +4,41 @@ title: 에디터 설정하기
-적절한 개발환경은 코드의 가독성을 높이며, 개발 속도를 높여줍니다. 심지어 코드를 작성하는 과정에서 버그를 찾아줄 수도 있습니다. 코드 에디터를 설치하는 것이 이번이 처음이거나, 현재 사용하는 에디터의 설정을 개선하고 싶으시다면 몇 가지 추천 사항이 있습니다.
+적절한 개발 환경은 코드의 가독성 및 개발 속도를 높여줍니다. 심지어 코드를 작성하는 과정에서 버그를 찾아줄 수도 있습니다. 코드 에디터를 설치하는 것이 이번이 처음이거나, 현재 사용하는 에디터의 설정을 개선하고 싶으시다면, 몇 가지를 추천드립니다.
* 어떤 에디터가 가장 유명한지
-* 어떻게 자동으로 코드를 포매팅하는지
+* 어떻게 자동으로 코드를 포맷팅하는지
## 에디터 {/*your-editor*/}
-[VS Code](https://code.visualstudio.com/)는 현재 가장 많이 사용되는 에디터 중 하나입니다. VS Code에 설치할 수 있는 익스텐션의 종류는 무수히 많으며, Github과 같은 외부 서비스와의 연동도 지원합니다. 아래에 나열된 기능들은 대부분 익스텐션으로 존재하기 때문에 VS Code의 설정은 다양한 방식으로 쉽게 변경할 수 있습니다.
+[VS Code](https://code.visualstudio.com/)는 현재 가장 많이 사용하는 에디터 중 하나입니다. VS Code에 설치할 수 있는 확장Extension의 종류는 무수히 많으며, [깃허브GitHub](https://github.com)와 같은 외부 서비스와의 연동도 지원합니다. 아래에 나열한 기능들은 대부분 확장Extension으로 존재하기 때문에 VS Code의 설정을 다양한 방식으로 쉽게 변경할 수 있습니다.
-그 외에도 React 커뮤니티에서는 다음과 같은 에디터들이 흔히 사용됩니다.
+이 외에도 React 커뮤니티에서는 다음과 같은 에디터들을 자주 사용합니다.
-* [WebStorm](https://www.jetbrains.com/ko-kr/webstorm/)은 JavaScript에 특화되어 설계된 통합 개발 환경입니다.
-* [Sublime Text](https://www.sublimetext.com/)는 JSX와 TypeScript를 지원하며 [문법 강조](https://stackoverflow.com/a/70960574/458193) 및 자동 완성 기능이 내장되어 있습니다.
+* [WebStorm](https://www.jetbrains.com/ko-kr/webstorm/)은 자바스크립트JavaScript에 특화되어 설계된 통합 개발 환경입니다.
+* [Sublime Text](https://www.sublimetext.com/)는 JSX와 타입스크립트TypeScript를 지원하며 [문법 강조](https://stackoverflow.com/a/70960574/458193) 및 자동 완성 기능이 내장되어 있습니다.
* [Vim](https://www.vim.org/)은 모든 종류의 텍스트를 매우 효율적으로 생성하고 변경할 수 있도록 설계된 텍스트 편집기입니다. 대부분의 UNIX 시스템과 Apple OS X에 "vi"로 포함되어 있습니다.
## 에디터 기능 추천 {/*recommended-text-editor-features*/}
-이러한 기능들이 기본으로 설정된 에디터들도 있지만, 별도의 익스텐션 추가가 필요한 경우도 존재합니다. 현재 사용 중인 에디터에서 어떠한 기능을 지원하는지 한번 확인해 보세요!
+이러한 기능들이 기본으로 설정된 에디터들도 있지만, 별도의 확장Extensions 추가가 필요한 경우도 존재합니다. 현재 사용 중인 에디터에서 어떠한 기능들을 지원하는지 한번 확인해 보세요!
-### 린팅 {/*linting*/}
+### 린팅Linting {/*linting*/}
-코드 린터는 코드를 작성하는 동안 실시간으로 문제를 찾아줌으로써 빠른 문제해결이 가능하도록 도와줍니다. [ESLint](https://eslint.org/)는 많이 사용되고 JavaScript를 위한 오픈소스 린터입니다.
+코드 린터Linter는 코드를 작성하는 동안 실시간으로 문제를 찾아, 빠른 문제 해결을 도와줍니다. 자바스크립트를 위한 오픈 소스 린터Linter인 [ESLint](https://eslint.org)를 가장 많이 사용합니다!
-* [React를 위한 추천 설정과 함께 ESLint 설치하기](https://www.npmjs.com/package/eslint-config-react-app) (사전에 [Node](https://nodejs.org/ko/download/current/)가 설치되어 있어야 합니다)
+* [React를 위한 추천 설정과 함께 ESLint 설치하기](https://www.npmjs.com/package/eslint-config-react-app) (사전에 [Node.js](https://nodejs.org/ko/download/current/)를 설치해야 합니다.)
* [VS Code의 ESLint를 공식 익스텐션과 통합하기](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
**프로젝트의 모든 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) 규칙을 활성화했는지 확인하세요.** 이 규칙은 필수적이며 가장 심각한 버그를 조기에 발견합니다. 권장되는 [`eslint-config-react-app`](https://www.npmjs.com/package/eslint-config-react-app) 프리셋에는 이미 이 규칙이 포함되어 있습니다.
-### 포매팅 {/*formatting*/}
+### 포맷팅Formatting {/*formatting*/}
다른 개발자들과 협업할 때 가장 피하고 싶은 것은 [탭 vs 공백](https://www.google.com/search?q=tabs+vs+spaces)에 대한 논쟁일 것입니다. 다행히 [Prettier](https://prettier.io/)를 사용하면 직접 지정해 놓은 규칙들에 부합하도록 코드의 형식을 깔끔하게 정리할 수 있습니다. Prettier를 실행하면 모든 탭은 공백으로 전환될 뿐만 아니라 들여쓰기, 따옴표 형식과 같은 요소들이 전부 설정에 부합하도록 수정될 것입니다. 파일을 저장할 때마다 Prettier가 자동 실행되어 이러한 작업들을 수행해 주는 것이 가장 이상적인 설정입니다.
@@ -49,9 +49,9 @@ title: 에디터 설정하기
3. `ext install esbenp.prettier-vscode`라고 입력하기
4. 엔터 누르기
-#### 저장 시점에 포매팅하기 {/*formatting-on-save*/}
+#### 저장 시점에 포맷팅하기 {/*formatting-on-save*/}
-저장할 때마다 코드가 포매팅 되는 것이 가장 이상적일 것입니다. 이러한 설정은 VS Code에 자체적으로 내장되어 있습니다!
+저장할 때마다 코드가 포맷팅 되는 것이 가장 이상적일 것입니다. 이러한 설정은 VS Code에 자체적으로 내장되어 있습니다!
1. VS Code에서 `CTRL/CMD + SHIFT + P` 누르기
2. "settings"라고 입력하기
@@ -59,4 +59,4 @@ title: 에디터 설정하기
4. 검색 창에서 "format on save"라고 입력하기
5. "format on save" 옵션이 제대로 체크되었는지 확인하세요!
-> 만약 ESLint 프리셋에 포매팅 규칙이 있는 경우 Prettier와 충돌을 일으킬 수도 있습니다. ESLint가 *오직* 논리적 실수를 잡는 데만 사용되도록 [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier)를 사용하여 ESLint 프리셋의 모든 포매팅 규칙을 비활성화하는 것을 권장합니다. Pull request가 merge 되기 전에 파일 형식이 지정되도록 하려면 지속적인 통합을 위해 [`prettier --check`](https://prettier.io/docs/en/cli.html#--check)를 사용하세요.
+> 만약 ESLint 프리셋에 포맷팅 규칙이 있는 경우 Prettier와 충돌을 일으킬 수도 있습니다. ESLint가 *오직* 논리적 실수를 잡는 데만 사용되도록 [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier)를 사용하여 ESLint 프리셋의 모든 포맷팅 규칙을 비활성화하는 것을 권장합니다. Pull Request가 병합Merge되기 전에 파일 형식이 지정되도록 하려면 지속적인 통합을 위해 [`prettier --check`](https://prettier.io/docs/en/cli.html#--check)를 사용하세요.
diff --git a/src/content/learn/index.md b/src/content/learn/index.md
index ae23d32f3..eacf1f4fb 100644
--- a/src/content/learn/index.md
+++ b/src/content/learn/index.md
@@ -21,7 +21,7 @@ React 문서에 오신 것을 환영합니다! 이 페이지에서는 여러분
## 컴포넌트 생성 및 중첩하기 {/*components*/}
-React 앱은 *컴포넌트*로 구성됩니다. 컴포넌트는 고유한 로직과 모양을 가진 UI(사용자 인터페이스)의 일부입니다. 컴포넌트는 버튼만큼 작을 수도 있고 전체 페이지만큼 클 수도 있습니다.
+React 앱은 *컴포넌트*로 구성됩니다. 컴포넌트는 고유한 로직과 모양을 가진 사용자 인터페이스UI의 일부입니다. 컴포넌트는 버튼만큼 작을 수도 있고 전체 페이지만큼 클 수도 있습니다.
React 컴포넌트는 마크업을 반환하는 자바스크립트 함수입니다.
@@ -115,7 +115,7 @@ React는 CSS 파일을 추가하는 방법을 규정하지 않습니다. 가장
## 데이터 표시하기 {/*displaying-data*/}
-JSX를 사용하면 자바스크립트에 마크업을 넣을 수 있습니다. 중괄호를 사용하면 코드에서 일부 변수를 삽입하여 사용자에게 표시할 수 있도록 자바스크립트로 "이스케이프 백(Escape Back)" 할 수 있습니다. 아래의 예시는 `user.name`을 표시합니다.
+JSX를 사용하면 자바스크립트에 마크업을 넣을 수 있습니다. 중괄호를 사용하면 코드에서 일부 변수를 삽입하여 사용자에게 표시할 수 있도록 자바스크립트로 "이스케이프 백Escape Back" 할 수 있습니다. 아래의 예시는 `user.name`을 표시합니다.
```js {3}
return (
@@ -125,7 +125,7 @@ return (
);
```
-JSX 어트리뷰트에서 따옴표 *대신* 중괄호를 사용하여 "자바스크립트로 이스케이프(Escape Into JavaScript)" 할 수도 있습니다. 예를 들어 `className="avatar"`는 `"avatar"` 문자열을 CSS로 전달하지만 `src={user.imageUrl}`는 자바스크립트 `user.imageUrl` 변수 값을 읽은 다음 해당 값을 `src` 어트리뷰트로 전달합니다.
+JSX 어트리뷰트에서 따옴표 *대신* 중괄호를 사용하여 "자바스크립트로 이스케이프Escape Into JavaScript" 할 수도 있습니다. 예를 들어 `className="avatar"`는 `"avatar"` 문자열을 CSS로 전달하지만 `src={user.imageUrl}`는 자바스크립트 `user.imageUrl` 변수 값을 읽은 다음 해당 값을 `src` 어트리뷰트로 전달합니다.
```js {3,4}
return (
@@ -299,7 +299,7 @@ function MyButton() {
## 화면 업데이트하기 {/*updating-the-screen*/}
-컴포넌트가 특정 정보를 "기억"하여 표시하기를 원하는 경우가 종종 있습니다. 예를 들어 버튼이 클릭된 횟수를 세고 싶을 수 있습니다. 이렇게 하려면 컴포넌트에 *state*를 추가하면 됩니다.
+컴포넌트가 특정 정보를 "기억"하여 표시하기를 원하는 경우가 종종 있습니다. 예를 들어 버튼이 클릭된 횟수를 세고 싶을 수 있습니다. 이렇게 하려면 컴포넌트에 *State*를 추가하면 됩니다.
먼저, React에서 [`useState`](/reference/react/useState)를 가져옵니다.
@@ -307,7 +307,7 @@ function MyButton() {
import { useState } from 'react';
```
-이제 컴포넌트 내부에 *state 변수*를 선언할 수 있습니다.
+이제 컴포넌트 내부에 *State 변수*를 선언할 수 있습니다.
```js
function MyButton() {
@@ -315,9 +315,9 @@ function MyButton() {
// ...
```
-`useState`로부터 현재 state (`count`)와 이를 업데이트할 수 있는 함수(`setCount`)를 얻을 수 있습니다. 이들을 어떤 이름으로도 지정할 수 있지만 `[something, setSomething]`으로 작성하는 것이 일반적입니다.
+`useState`로부터 현재 State (`count`)와 이를 업데이트할 수 있는 함수 (`setCount`)를 얻을 수 있습니다. 이들을 어떤 이름으로도 지정할 수 있지만 `[something, setSomething]`으로 작성하는 것이 일반적입니다.
-버튼이 처음 표시될 때는 `useState()`에 `0`을 전달했기 때문에 `count`가 `0`이 됩니다. state를 변경하고 싶다면 `setCount()`를 실행하고 새 값을 전달하세요. 이 버튼을 클릭하면 카운터가 증가합니다.
+버튼이 처음 표시될 때는 `useState()`에 `0`을 전달했기 때문에 `count`가 `0`이 됩니다. State를 변경하고 싶다면 `setCount()`를 실행하고 새 값을 전달하세요. 이 버튼을 클릭하면 카운터가 증가합니다.
```js {5}
function MyButton() {
@@ -337,7 +337,7 @@ function MyButton() {
React가 컴포넌트 함수를 다시 호출합니다. 이번에는 `count`가 `1`이 되고, 그 다음에는 `2`가 될 것입니다. 이런 방식입니다.
-같은 컴포넌트를 여러 번 렌더링하면 각각의 컴포넌트는 고유한 state를 얻게 됩니다. 각 버튼을 개별적으로 클릭해 보세요.
+같은 컴포넌트를 여러 번 렌더링하면 각각의 컴포넌트는 고유한 State를 얻게 됩니다. 각 버튼을 개별적으로 클릭해 보세요.
@@ -378,7 +378,7 @@ button {
-각 버튼이 고유한 `count` state를 "기억"하고 다른 버튼에 영향을 주지 않는 방식에 주목해 주세요.
+각 버튼이 고유한 `count` State를 "기억"하고 다른 버튼에 영향을 주지 않는 방식에 주목해 주세요.
## Hook 사용하기 {/*using-hooks*/}
@@ -408,7 +408,7 @@ Hook은 다른 함수보다 더 제한적입니다. 컴포넌트(또는 다른 H
하지만 *데이터를 공유하고 항상 함께 업데이트하기* 위한 컴포넌트가 필요한 경우가 많습니다.
-두 `MyButton` 컴포넌트가 동일한 `count`를 표시하고 함께 업데이트하려면, state를 개별 버튼에서 모든 버튼이 포함된 가장 가까운 컴포넌트로 "위쪽"으로 이동해야 합니다.
+두 `MyButton` 컴포넌트가 동일한 `count`를 표시하고 함께 업데이트하려면, State를 개별 버튼에서 모든 버튼이 포함된 가장 가까운 컴포넌트로 "위쪽"으로 이동해야 합니다.
이 예시에서는 `MyApp`입니다.
@@ -416,13 +416,13 @@ Hook은 다른 함수보다 더 제한적입니다. 컴포넌트(또는 다른 H
-처음에 `MyApp`의 `count` state는 `0`이며 두 자식에게 모두 전달됩니다.
+처음에 `MyApp`의 `count` State는 `0`이며 두 자식에게 모두 전달합니다.
-클릭 시 `MyApp`은 `count` state를 `1`로 업데이트하고 두 자식에게 전달합니다.
+클릭 시 `MyApp`은 `count` State를 `1`로 업데이트하고 두 자식에게 전달합니다.
@@ -430,7 +430,7 @@ Hook은 다른 함수보다 더 제한적입니다. 컴포넌트(또는 다른 H
이제 두 버튼 중 하나를 클릭하면 `MyApp`의 `count`가 변경되어 `MyButton`의 카운트가 모두 변경됩니다. 이를 코드로 표현하는 방법은 다음과 같습니다.
-먼저 `MyButton`에서 `MyApp`으로 *state를 위로 이동*합니다.
+먼저 `MyButton`에서 `MyApp`으로 *State를 위로 이동*합니다.
```js {2-6,18}
export default function MyApp() {
@@ -455,7 +455,7 @@ function MyButton() {
```
-그 다음 공유된 클릭 핸들러와 함께 `MyApp`에서 각 `MyButton`으로 *state를 전달합니다*. 이전에 ``와 같은 기본 제공 태그를 사용했던 것처럼 JSX 중괄호를 사용하여 `MyButton`에 정보를 전달할 수 있습니다.
+그 다음 공유된 클릭 핸들러와 함께 `MyApp`에서 각 `MyButton`으로 *State를 전달합니다*. 이전에 ``와 같은 기본 제공 태그를 사용했던 것처럼 JSX 중괄호를 사용하여 `MyButton`에 정보를 전달할 수 있습니다.
```js {11-12}
export default function MyApp() {
@@ -475,9 +475,9 @@ export default function MyApp() {
}
```
-이렇게 전달한 정보를 *props*라고 합니다. 이제 `MyApp` 컴포넌트는 `count` state와 `handleClick` 이벤트 핸들러를 포함하며, *이 두 가지를 각 버튼에 props로 전달합니다*.
+이렇게 전달한 정보를 *Props*라고 합니다. 이제 `MyApp` 컴포넌트는 `count` State와 `handleClick` 이벤트 핸들러를 포함하며, *이 두 가지를 각 버튼에 Props로 전달합니다*.
-마지막으로 부모 컴포넌트에서 전달한 props를 *읽도록* `MyButton`을 변경합니다.
+마지막으로 부모 컴포넌트에서 전달한 Props를 *읽도록* `MyButton`을 변경합니다.
```js {1,3}
@@ -490,7 +490,7 @@ function MyButton({ count, onClick }) {
}
```
-버튼을 클릭하면 `onClick` 핸들러가 실행됩니다. 각 버튼의 `onClick` prop는 `MyApp` 내부의 `handleClick` 함수로 설정되었으므로 그 안에 있는 코드가 실행됩니다. 이 코드는 `setCount(count + 1)`를 실행하여 `count` state 변수를 증가시킵니다. 새로운 `count` 값은 각 버튼에 prop로 전달되므로 모든 버튼에는 새로운 값이 표시됩니다. 이를 "state 끌어올리기"라고 합니다. state를 위로 이동함으로써 컴포넌트 간에 state를 공유하게 됩니다.
+버튼을 클릭하면 `onClick` 핸들러가 실행됩니다. 각 버튼의 `onClick` Prop는 `MyApp` 내부의 `handleClick` 함수로 설정되었으므로 그 안에 있는 코드가 실행됩니다. 이 코드는 `setCount(count + 1)`를 실행하여 `count` State 변수를 증가시킵니다. 새로운 `count` 값은 각 버튼에 Prop로 전달되므로 모든 버튼에는 새로운 값이 표시됩니다. 이를 "State 끌어올리기"라고 합니다. State를 위로 이동함으로써 컴포넌트 간에 State를 공유하게 됩니다.
diff --git a/src/content/learn/preserving-and-resetting-state.md b/src/content/learn/preserving-and-resetting-state.md
index 770e416d1..b23d99752 100644
--- a/src/content/learn/preserving-and-resetting-state.md
+++ b/src/content/learn/preserving-and-resetting-state.md
@@ -2006,7 +2006,7 @@ export default function ContactList() {