@samitouri / QOS-React / commits / 68f00c901c

Release Activity in Canary (#34374)

## Overview This PR ships `<Activity />` to the `react@canary` release channel for final feedback and prepare for semver stable release. ## What this means Shipping `<Activity />` to canary means it has gone through extensive testing in production, we are confident in the stability of the feature, and we are preparing to release it in a future semver stable version. Libraries and frameworks following the [Canary Workflow](https://react.dev/blog/2023/05/03/react-canaries) should begin implementing and testing the feature. ## Why we follow the Canary Workflow To prepare for semver stable, libraries should test canary features like `<Activity>` with `react@canary` to confirm compatibility and prepare for the next semver release in a myriad of environments and configurations used throughout the React ecosystem. This provides libraries with ample time to catch any issues we missed before slamming them with problems in the wider semver release. Since these features have already gone through extensive production testing, and we are confident they are stable, frameworks following the [Canary Workflow](https://react.dev/blog/2023/05/03/react-canaries) can also begin adopting canary features like `<Activity />`. This adoption is similar to how different Browsers implement new proposed browser features before they are added to the standard. If a frameworks adopts a canary feature, they are committing to stability for their users by ensuring any API changes before a semver stable release are opaque and non-breaking to their users. Apps not using a framework are also free to adopt canary features like Activity as long as they follow the [Canary Workflow](https://react.dev/blog/2023/05/03/react-canaries), but we generally recommend waiting for a semver stable release unless you have the capacity to commit to following along with the canary changes and debugging library compatibility issues. Waiting for semver stable means you're able to benefit from libraries testing and confirming support, and use semver as signal for which version of a library you can use with support of the feature. ## Docs Check out the ["React Labs: View Transitions, Activity, and more"](https://react.dev/blog/2025/04/23/react-labs-view-transitions-activity-and-more#activity) blog post, and [the new docs for `<Activity>`](https://react.dev/reference/react/Activity) for more info. ## TODO - [x] Bump Activity docs to Canary https://github.com/reactjs/react.dev/pull/7974 --------- Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>

Ricky committed Sep 12, 2025 at 12:47 UTC 68f00c901c05e3a91f6cc77b660bc2334700f163
28 files changed +41 -36
fixtures/view-transition/src/components/Page.js
+1 -1
@@ -1,7 +1,7 @@
1 import React, {
2 unstable_addTransitionType as addTransitionType,
3 unstable_ViewTransition as ViewTransition,
4 - unstable_Activity as Activity,
4 + Activity,
5 useLayoutEffect,
6 useEffect,
7 useState,
packages/react-devtools-shared/src/__tests__/storeComponentFilters-test.js
+1 -1
@@ -199,7 +199,7 @@ describe('Store component filters', () => {
199 });
200
201 it('should filter Activity', async () => {
202 - const Activity = React.unstable_Activity;
202 + const Activity = React.Activity || React.unstable_Activity;
203
204 if (Activity != null) {
205 await actAsync(async () =>
packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js
+1 -1
@@ -31,7 +31,7 @@ describe('FragmentRefs', () => {
31 jest.resetModules();
32 React = require('react');
33 Fragment = React.Fragment;
34 - Activity = React.unstable_Activity;
34 + Activity = React.Activity;
35 ReactDOMClient = require('react-dom/client');
36 ReactDOM = require('react-dom');
37 createPortal = ReactDOM.createPortal;
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
+1 -1
@@ -113,7 +113,7 @@ describe('ReactDOMServerPartialHydration', () => {
113 act = require('internal-test-utils').act;
114 ReactDOMServer = require('react-dom/server');
115 Scheduler = require('scheduler');
116 - Activity = React.unstable_Activity;
116 + Activity = React.Activity;
117 Suspense = React.Suspense;
118 useSyncExternalStore = React.useSyncExternalStore;
119 if (gate(flags => flags.enableSuspenseList)) {
packages/react-dom/src/__tests__/ReactDOMServerPartialHydrationActivity-test.internal.js
+1 -1
@@ -100,7 +100,7 @@ describe('ReactDOMServerPartialHydrationActivity', () => {
100 act = require('internal-test-utils').act;
101 ReactDOMServer = require('react-dom/server');
102 Scheduler = require('scheduler');
103 - Activity = React.unstable_Activity;
103 + Activity = React.Activity;
104 Suspense = React.Suspense;
105 useSyncExternalStore = React.useSyncExternalStore;
106
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydrationActivity-test.internal.js
+1 -1
@@ -139,7 +139,7 @@ describe('ReactDOMServerSelectiveHydrationActivity', () => {
139 ReactDOMServer = require('react-dom/server');
140 act = require('internal-test-utils').act;
141 Scheduler = require('scheduler');
142 - Activity = React.unstable_Activity;
142 + Activity = React.Activity;
143
144 const InternalTestUtils = require('internal-test-utils');
145 assertLog = InternalTestUtils.assertLog;
packages/react-reconciler/src/__tests__/Activity-test.js
+2 -2
@@ -24,7 +24,7 @@ describe('Activity', () => {
24 Scheduler = require('scheduler');
25 act = require('internal-test-utils').act;
26 LegacyHidden = React.unstable_LegacyHidden;
27 - Activity = React.unstable_Activity;
27 + Activity = React.Activity;
28 useState = React.useState;
29 useInsertionEffect = React.useInsertionEffect;
30 useLayoutEffect = React.useLayoutEffect;
@@ -280,7 +280,7 @@ describe('Activity', () => {
280
281 // @gate enableActivity
282 it('nested offscreen does not call componentWillUnmount when hidden', async () => {
283 - // This is a bug that appeared during production test of <unstable_Activity />.
283 + // This is a bug that appeared during production test of <Activity />.
284 // It is a very specific scenario with nested Offscreens. The inner offscreen
285 // goes from visible to hidden in synchronous update.
286 class ClassComponent extends React.Component {
packages/react-reconciler/src/__tests__/ActivityLegacySuspense-test.js
+1 -1
@@ -22,7 +22,7 @@ describe('Activity Suspense', () => {
22 Scheduler = require('scheduler');
23 act = require('internal-test-utils').act;
24 LegacyHidden = React.unstable_LegacyHidden;
25 - Activity = React.unstable_Activity;
25 + Activity = React.Activity;
26 Suspense = React.Suspense;
27 useState = React.useState;
28 useEffect = React.useEffect;
packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js
+1 -1
@@ -10,7 +10,7 @@ describe('Activity StrictMode', () => {
10 log = [];
11
12 React = require('react');
13 - Activity = React.unstable_Activity;
13 + Activity = React.Activity;
14 ReactNoop = require('react-noop-renderer');
15 act = require('internal-test-utils').act;
16 });
packages/react-reconciler/src/__tests__/ActivitySuspense-test.js
+1 -1
@@ -23,7 +23,7 @@ describe('Activity Suspense', () => {
23 Scheduler = require('scheduler');
24 act = require('internal-test-utils').act;
25 LegacyHidden = React.unstable_LegacyHidden;
26 - Activity = React.unstable_Activity;
26 + Activity = React.Activity;
27 Suspense = React.Suspense;
28 useState = React.useState;
29 useEffect = React.useEffect;
packages/react-reconciler/src/__tests__/ReactDeferredValue-test.js
+1 -1
@@ -34,7 +34,7 @@ describe('ReactDeferredValue', () => {
34 useMemo = React.useMemo;
35 useState = React.useState;
36 Suspense = React.Suspense;
37 - Activity = React.unstable_Activity;
37 + Activity = React.Activity;
38
39 const InternalTestUtils = require('internal-test-utils');
40 assertLog = InternalTestUtils.assertLog;
packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js
+1 -1
@@ -28,7 +28,7 @@ describe('ReactFragment', () => {
28
29 React = require('react');
30 Suspense = React.Suspense;
31 - Activity = React.unstable_Activity;
31 + Activity = React.Activity;
32 ViewTransition = React.unstable_ViewTransition;
33 ReactNoop = require('react-noop-renderer');
34 const InternalTestUtils = require('internal-test-utils');
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+1 -1
@@ -68,7 +68,7 @@ describe('ReactHooksWithNoopRenderer', () => {
68 useTransition = React.useTransition;
69 useDeferredValue = React.useDeferredValue;
70 Suspense = React.Suspense;
71 - Activity = React.unstable_Activity;
71 + Activity = React.Activity;
72 ContinuousEventPriority =
73 require('react-reconciler/constants').ContinuousEventPriority;
74 if (gate(flags => flags.enableSuspenseList)) {
packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
+2 -2
@@ -968,7 +968,7 @@ describe('ReactLazy', () => {
968
969 // @gate enableActivity
970 it('throws with a useful error when wrapping Activity with lazy()', async () => {
971 - const BadLazy = lazy(() => fakeImport(React.unstable_Activity));
971 + const BadLazy = lazy(() => fakeImport(React.Activity));
972
973 const root = ReactTestRenderer.create(
974 <Suspense fallback={<Text text="Loading..." />}>
@@ -981,7 +981,7 @@ describe('ReactLazy', () => {
981
982 await waitForAll(['Loading...']);
983
984 - await resolveFakeImport(React.unstable_Activity);
984 + await resolveFakeImport(React.Activity);
985 root.update(
986 <Suspense fallback={<Text text="Loading..." />}>
987 <BadLazy />
packages/react-reconciler/src/__tests__/ReactSiblingPrerendering-test.js
+1 -1
@@ -25,7 +25,7 @@ describe('ReactSiblingPrerendering', () => {
25 waitForAll = require('internal-test-utils').waitForAll;
26 startTransition = React.startTransition;
27 Suspense = React.Suspense;
28 - Activity = React.unstable_Activity;
28 + Activity = React.Activity;
29
30 textCache = new Map();
31 });
packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js
+1 -1
@@ -23,7 +23,7 @@ describe('ReactSuspenseyCommitPhase', () => {
23 if (gate(flags => flags.enableSuspenseList)) {
24 SuspenseList = React.unstable_SuspenseList;
25 }
26 - Activity = React.unstable_Activity;
26 + Activity = React.Activity;
27 useMemo = React.useMemo;
28 startTransition = React.startTransition;
29 resolveSuspenseyThing = ReactNoop.resolveSuspenseyThing;
packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js
+1 -1
@@ -53,7 +53,7 @@ describe('ReactInteractionTracing', () => {
53 useState = React.useState;
54 startTransition = React.startTransition;
55 Suspense = React.Suspense;
56 - Activity = React.unstable_Activity;
56 + Activity = React.Activity;
57
58 getCacheForType = React.unstable_getCacheForType;
59
packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
+9 -9
@@ -308,7 +308,7 @@ describe('ReactFreshIntegration', () => {
308 // @gate __DEV__ && enableActivity
309 it('ignores ref for class component in hidden subtree', async () => {
310 const code = `
311 - import {unstable_Activity as Activity} from 'react';
311 + import {Activity} from 'react';
312
313 // Avoid creating a new class on Fast Refresh.
314 global.A = global.A ?? class A extends React.Component {
@@ -338,7 +338,7 @@ describe('ReactFreshIntegration', () => {
338 // @gate __DEV__ && enableActivity
339 it('ignores ref for hoistable resource in hidden subtree', async () => {
340 const code = `
341 - import {unstable_Activity as Activity} from 'react';
341 + import {Activity} from 'react';
342
343 function hiddenRef() {
344 throw new Error('Unexpected hiddenRef() invocation.');
@@ -360,7 +360,7 @@ describe('ReactFreshIntegration', () => {
360 // @gate __DEV__ && enableActivity
361 it('ignores ref for host component in hidden subtree', async () => {
362 const code = `
363 - import {unstable_Activity as Activity} from 'react';
363 + import {Activity} from 'react';
364
365 function hiddenRef() {
366 throw new Error('Unexpected hiddenRef() invocation.');
@@ -382,7 +382,7 @@ describe('ReactFreshIntegration', () => {
382 // @gate __DEV__ && enableActivity
383 it('ignores ref for Activity in hidden subtree', async () => {
384 const code = `
385 - import {unstable_Activity as Activity} from 'react';
385 + import {Activity} from 'react';
386
387 function hiddenRef(value) {
388 throw new Error('Unexpected hiddenRef() invocation.');
@@ -407,7 +407,7 @@ describe('ReactFreshIntegration', () => {
407 it('ignores ref for Scope in hidden subtree', async () => {
408 const code = `
409 import {
410 - unstable_Activity as Activity,
410 + Activity,
411 unstable_Scope as Scope,
412 } from 'react';
413
@@ -433,7 +433,7 @@ describe('ReactFreshIntegration', () => {
433 // @gate __DEV__ && enableActivity
434 it('ignores ref for functional component in hidden subtree', async () => {
435 const code = `
436 - import {unstable_Activity as Activity} from 'react';
436 + import {Activity} from 'react';
437
438 // Avoid creating a new component on Fast Refresh.
439 global.A = global.A ?? function A() {
@@ -463,7 +463,7 @@ describe('ReactFreshIntegration', () => {
463 const code = `
464 import {
465 forwardRef,
466 - unstable_Activity as Activity,
466 + Activity,
467 } from 'react';
468
469 // Avoid creating a new component on Fast Refresh.
@@ -494,7 +494,7 @@ describe('ReactFreshIntegration', () => {
494 const code = `
495 import {
496 memo,
497 - unstable_Activity as Activity,
497 + Activity,
498 } from 'react';
499
500 // Avoid creating a new component on Fast Refresh.
@@ -526,7 +526,7 @@ describe('ReactFreshIntegration', () => {
526 const code = `
527 import {
528 memo,
529 - unstable_Activity as Activity,
529 + Activity,
530 } from 'react';
531
532 // Avoid creating a new component on Fast Refresh.
packages/react/index.development.js
+1 -1
@@ -44,7 +44,7 @@ export {
44 cacheSignal,
45 startTransition,
46 unstable_LegacyHidden,
47 - unstable_Activity,
47 + Activity,
48 unstable_Scope,
49 unstable_SuspenseList,
50 unstable_TracingMarker,
packages/react/index.experimental.development.js
+1 -1
@@ -29,7 +29,7 @@ export {
29 cache,
30 cacheSignal,
31 startTransition,
32 - unstable_Activity,
32 + Activity,
33 unstable_postpone,
34 unstable_getCacheForType,
35 unstable_SuspenseList,
packages/react/index.experimental.js
+2 -1
@@ -29,7 +29,8 @@ export {
29 cache,
30 cacheSignal,
31 startTransition,
32 - unstable_Activity,
32 + Activity,
33 + Activity as unstable_Activity,
34 unstable_postpone,
35 unstable_getCacheForType,
36 unstable_SuspenseList,
packages/react/index.fb.js
+2 -1
@@ -32,7 +32,8 @@ export {
32 startTransition,
33 StrictMode,
34 Suspense,
35 - unstable_Activity,
35 + Activity,
36 + Activity as unstable_Activity,
37 unstable_getCacheForType,
38 unstable_LegacyHidden,
39 unstable_Scope,
packages/react/index.js
+1 -1
@@ -44,7 +44,7 @@ export {
44 cacheSignal,
45 startTransition,
46 unstable_LegacyHidden,
47 - unstable_Activity,
47 + Activity,
48 unstable_Scope,
49 unstable_SuspenseList,
50 unstable_TracingMarker,
packages/react/index.stable.development.js
+1
@@ -10,6 +10,7 @@
10 export {
11 __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
12 __COMPILER_RUNTIME,
13 + Activity,
14 Children,
15 Component,
16 Fragment,
packages/react/index.stable.js
+1
@@ -10,6 +10,7 @@
10 export {
11 __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
12 __COMPILER_RUNTIME,
13 + Activity,
14 Children,
15 Component,
16 Fragment,
packages/react/src/ReactClient.js
+1 -1
@@ -116,7 +116,7 @@ export {
116 useDeferredValue,
117 REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList,
118 REACT_LEGACY_HIDDEN_TYPE as unstable_LegacyHidden,
119 - REACT_ACTIVITY_TYPE as unstable_Activity,
119 + REACT_ACTIVITY_TYPE as Activity,
120 getCacheForType as unstable_getCacheForType,
121 useCacheRefresh as unstable_useCacheRefresh,
122 use,
packages/react/src/ReactServer.experimental.js
+1 -1
@@ -82,5 +82,5 @@ export {
82 // Experimental
83 REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList,
84 REACT_VIEW_TRANSITION_TYPE as unstable_ViewTransition,
85 - REACT_ACTIVITY_TYPE as unstable_Activity,
85 + REACT_ACTIVITY_TYPE as Activity,
86 };
scripts/jest/TestFlags.js
+2 -1
@@ -81,7 +81,8 @@ function getTestFlags() {
81 fb: www || xplat,
82
83 // These aren't flags, just a useful aliases for tests.
84 - enableActivity: releaseChannel === 'experimental' || www || xplat,
84 + // TODO: Clean this up.
85 + enableActivity: true,
86 enableSuspenseList: releaseChannel === 'experimental' || www || xplat,
87 enableLegacyHidden: www,
88 // TODO: Suspending the work loop during the render phase is currently