Skip to content

Commit 73fcb9b

Browse files
authored
Stabilize viewTransiton and flushSync options (#11989)
1 parent 2dd13c6 commit 73fcb9b

18 files changed

+143
-130
lines changed

‎.changeset/stabilize-flush-sync.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"react-router-dom": minor
3+
"react-router": minor
4+
"@remix-run/router": minor
5+
---
6+
7+
Stabilize the `unstable_flushSync` option for navigations and fetchers
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"react-router-dom": minor
3+
"react-router": minor
4+
"@remix-run/router": minor
5+
---
6+
7+
Stabilize the `unstable_viewTransition` option for navigations and the corresponding `unstable_useViewTransitionState` hook

‎docs/components/form.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface FormProps
2727
reloadDocument?: boolean;
2828
replace?: boolean;
2929
state?: any;
30-
unstable_viewTransition?: boolean;
30+
viewTransition?: boolean;
3131
}
3232
```
3333

@@ -281,9 +281,9 @@ If you are using [`<ScrollRestoration>`][scrollrestoration], this lets you preve
281281

282282
See also: [`<Link preventScrollReset>`][link-preventscrollreset]
283283

284-
## `unstable_viewTransition`
284+
## `viewTransition`
285285

286-
The `unstable_viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`. If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state].
286+
The `viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`. If you need to apply specific styles for this view transition, you will also need to leverage the [`useViewTransitionState()`][use-view-transition-state].
287287

288288
<docs-warning>Please note that this API is marked unstable and may be subject to breaking changes without a major release</docs-warning>
289289

‎docs/components/link.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ interface LinkProps
2323
reloadDocument?: boolean;
2424
replace?: boolean;
2525
state?: any;
26-
unstable_viewTransition?: boolean;
26+
viewTransition?: boolean;
2727
}
2828

2929
type To = string | Partial<Path>;
@@ -171,24 +171,24 @@ let { state } = useLocation();
171171

172172
The `reloadDocument` property can be used to skip client side routing and let the browser handle the transition normally (as if it were an `<a href>`).
173173

174-
## `unstable_viewTransition`
174+
## `viewTransition`
175175

176-
The `unstable_viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`:
176+
The `viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`:
177177

178178
```jsx
179-
<Link to={to} unstable_viewTransition>
179+
<Link to={to} viewTransition>
180180
Click me
181181
</Link>
182182
```
183183

184-
If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state] hook (or check out the `transitioning` class and `isTransitioning` render prop in [NavLink][navlink]):
184+
If you need to apply specific styles for this view transition, you will also need to leverage the [`useViewTransitionState()`][use-view-transition-state] hook (or check out the `transitioning` class and `isTransitioning` render prop in [NavLink][navlink]):
185185

186186
```jsx
187187
function ImageLink(to) {
188188
const isTransitioning =
189-
unstable_useViewTransitionState(to);
189+
useViewTransitionState(to);
190190
return (
191-
<Link to={to} unstable_viewTransition>
191+
<Link to={to} viewTransition>
192192
<p
193193
style={{
194194
viewTransitionName: isTransitioning
@@ -212,7 +212,7 @@ function ImageLink(to) {
212212
}
213213
```
214214

215-
<docs-warning>`unstable_viewTransition` only works when using a data router, see [Picking a Router][picking-a-router]</docs-warning>
215+
<docs-warning>`viewTransition` only works when using a data router, see [Picking a Router][picking-a-router]</docs-warning>
216216

217217
<docs-warning>Please note that this API is marked unstable and may be subject to breaking changes without a major release</docs-warning>
218218

‎docs/components/nav-link.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ When a `NavLink` is active it will automatically apply `<a aria-current="page">`
123123

124124
The `reloadDocument` property can be used to skip client side routing and let the browser handle the transition normally (as if it were an `<a href>`).
125125

126-
## `unstable_viewTransition`
126+
## `viewTransition`
127127

128-
The `unstable_viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`. By default, during the transition a `transitioning` class will be added to the `<a>` element that you can use to customize the view transition.
128+
The `viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`. By default, during the transition a `transitioning` class will be added to the `<a>` element that you can use to customize the view transition.
129129

130130
```css
131131
a.transitioning p {
@@ -138,7 +138,7 @@ a.transitioning img {
138138
```
139139

140140
```jsx
141-
<NavLink to={to} unstable_viewTransition>
141+
<NavLink to={to} viewTransition>
142142
<p>Image Number {idx}</p>
143143
<img src={src} alt={`Img ${idx}`} />
144144
</NavLink>
@@ -147,7 +147,7 @@ a.transitioning img {
147147
You may also use the `className`/`style` props or the render props passed to `children` to further customize based on the `isTransitioning` value.
148148

149149
```jsx
150-
<NavLink to={to} unstable_viewTransition>
150+
<NavLink to={to} viewTransition>
151151
{({ isTransitioning }) => (
152152
<>
153153
<p

‎docs/hooks/use-fetcher.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,9 @@ If you find yourself calling this function inside of click handlers, you can pro
133133

134134
<docs-info>Any `fetcher.load` calls that are active on the page will be re-executed as part of revalidation (either after a navigation submission, another fetcher submission, or a `useRevalidator()` call)</docs-info>
135135

136-
#### `options.unstable_flushSync`
136+
#### `options.flushSync`
137137

138-
The `unstable_flushSync` option tells React Router DOM to wrap the initial state update for this `fetcher.load` in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.
138+
The `flushSync` option tells React Router DOM to wrap the initial state update for this `fetcher.load` in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.
139139

140140
<docs-warning>Please note that this API is marked unstable and may be subject to breaking changes without a major release</docs-warning>
141141

‎docs/hooks/use-navigate.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ interface NavigateOptions {
2020
state?: any;
2121
preventScrollReset?: boolean;
2222
relative?: RelativeRoutingType;
23-
unstable_flushSync?: boolean;
24-
unstable_viewTransition?: boolean;
23+
flushSync?: boolean;
24+
viewTransition?: boolean;
2525
}
2626

2727
type RelativeRoutingType = "route" | "path";
@@ -114,19 +114,19 @@ new URL("..", window.origin + location.pathname + "/");
114114
// 'https://remix.run/docs/en/main/start/'
115115
```
116116

117-
## `options.unstable_flushSync`
117+
## `options.flushSync`
118118

119-
The `unstable_flushSync` option tells React Router DOM to wrap the initial state update for this navigation in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.
119+
The `flushSync` option tells React Router DOM to wrap the initial state update for this navigation in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.
120120

121-
<docs-warning>`unstable_flushSync` only works when using a data router, see [Picking a Router][picking-a-router]</docs-warning>
121+
<docs-warning>`flushSync` only works when using a data router, see [Picking a Router][picking-a-router]</docs-warning>
122122

123123
<docs-warning>Please note that this API is marked unstable and may be subject to breaking changes without a major release</docs-warning>
124124

125-
## `options.unstable_viewTransition`
125+
## `options.viewTransition`
126126

127-
The `unstable_viewTransition` option enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`. If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state].
127+
The `viewTransition` option enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`. If you need to apply specific styles for this view transition, you will also need to leverage the [`useViewTransitionState()`][use-view-transition-state].
128128

129-
<docs-warning>`unstable_viewTransition` only works when using a data router, see [Picking a Router][picking-a-router]</docs-warning>
129+
<docs-warning>`viewTransition` only works when using a data router, see [Picking a Router][picking-a-router]</docs-warning>
130130

131131
<docs-warning>Please note that this API is marked unstable and may be subject to breaking changes without a major release</docs-warning>
132132

‎docs/hooks/use-submit.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,11 @@ Because submissions are navigations, the options may also contain the other navi
160160
- `relative`
161161
- `replace`
162162
- `state`
163-
- `unstable_viewTransition`
163+
- `viewTransition`
164164

165-
### `options.unstable_flushSync`
165+
### `options.flushSync`
166166

167-
The `unstable_flushSync` option tells React Router DOM to wrap the initial state update for this submission in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.
167+
The `flushSync` option tells React Router DOM to wrap the initial state update for this submission in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.
168168

169169
<docs-warning>Please note that this API is marked unstable and may be subject to breaking changes without a major release</docs-warning>
170170

‎docs/hooks/use-view-transition-state.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
---
2-
title: unstable_useViewTransitionState
2+
title: useViewTransitionState
33
---
44

5-
# `unstable_useViewTransitionState`
5+
# `useViewTransitionState`
66

77
<details>
88
<summary>Type declaration</summary>
99

1010
```tsx
11-
declare function unstable_useViewTransitionState(
11+
declare function useViewTransitionState(
1212
to: To,
1313
opts: { relative?: "route" : "path" } = {}
1414
): boolean;
@@ -24,17 +24,16 @@ interface Path {
2424

2525
</details>
2626

27-
This hook returns `true` when there is an active [View Transition][view-transitions] to the specified location. This can be used to apply finer-grained styles to elements to further customize the view transition. This requires that view transitions have been enabled for the given navigation via the [unstable_viewTransition][link-view-transition] prop on the `Link` (or the `Form`, `navigate`, or `submit` call).
27+
This hook returns `true` when there is an active [View Transition][view-transitions] to the specified location. This can be used to apply finer-grained styles to elements to further customize the view transition. This requires that view transitions have been enabled for the given navigation via the [viewTransition][link-view-transition] prop on the `Link` (or the `Form`, `navigate`, or `submit` call).
2828

2929
Consider clicking on an image in a list that you need to expand into the hero image on the destination page:
3030

3131
```jsx
3232
function NavImage({ src, alt, id }) {
3333
const to = `/images/${id}`;
34-
const isTransitioning =
35-
unstable_useViewTransitionState(to);
34+
const isTransitioning = useViewTransitionState(to);
3635
return (
37-
<Link to={to} unstable_viewTransition>
36+
<Link to={to} viewTransition>
3837
<img
3938
src={src}
4039
alt={alt}
@@ -49,5 +48,5 @@ function NavImage({ src, alt, id }) {
4948
}
5049
```
5150

52-
[link-view-transition]: ../components/link#unstable_viewtransition
51+
[link-view-transition]: ../components/link#viewtransition
5352
[view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API

‎docs/routers/picking-a-router.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,5 @@ The following APIs are introduced in React Router 6.4 and will only work when us
146146
[userouteloaderdata]: ../hooks/use-route-loader-data
147147
[usesubmit]: ../hooks/use-submit
148148
[useblocker]: ../hooks/use-blocker
149-
[viewtransition-link]: ../components/link#unstable_viewtransition
150-
[viewtransition-navigate]: ../hooks/use-navigate#optionsunstable_viewtransition
149+
[viewtransition-link]: ../components/link#viewtransition
150+
[viewtransition-navigate]: ../hooks/use-navigate#optionsviewtransition

‎packages/react-router-dom/__tests__/data-browser-router-test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7405,13 +7405,13 @@ function testDomRouter(
74057405
return (
74067406
<div>
74077407
<Link to="/a">/a</Link>
7408-
<Link to="/b" unstable_viewTransition>
7408+
<Link to="/b" viewTransition>
74097409
/b
74107410
</Link>
74117411
<Form action="/c">
74127412
<button type="submit">/c</button>
74137413
</Form>
7414-
<Form action="/d" unstable_viewTransition>
7414+
<Form action="/d" viewTransition>
74157415
<button type="submit">/d</button>
74167416
</Form>
74177417
<Outlet />
@@ -7486,7 +7486,7 @@ function testDomRouter(
74867486
Component() {
74877487
return (
74887488
<>
7489-
<Link to="/page" unstable_viewTransition>
7489+
<Link to="/page" viewTransition>
74907490
/page
74917491
</Link>
74927492
<Outlet />

‎packages/react-router-dom/__tests__/flush-sync-navigations-test.tsx

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ describe("flushSync", () => {
3333
<>
3434
<h1>About</h1>
3535
<button
36-
onClick={() => navigate("/", { unstable_flushSync: true })}
36+
onClick={() => navigate("/", { flushSync: true })}
3737
>
3838
Go to /
3939
</button>
@@ -61,14 +61,14 @@ describe("flushSync", () => {
6161
expect(spy).toBeCalledTimes(1);
6262
expect(spy).toHaveBeenLastCalledWith(
6363
expect.anything(),
64-
expect.objectContaining({ unstable_flushSync: false })
64+
expect.objectContaining({ flushSync: false })
6565
);
6666

6767
fireEvent.click(screen.getByText("Go to /"));
6868
await waitFor(() => screen.getByText("Home"));
6969
expect(spy).toHaveBeenLastCalledWith(
7070
expect.anything(),
71-
expect.objectContaining({ unstable_flushSync: true })
71+
expect.objectContaining({ flushSync: true })
7272
);
7373

7474
expect(spy).toBeCalledTimes(2);
@@ -110,7 +110,7 @@ describe("flushSync", () => {
110110
onClick={() =>
111111
submit(
112112
{},
113-
{ method: "post", action: "/", unstable_flushSync: true }
113+
{ method: "post", action: "/", flushSync: true }
114114
)
115115
}
116116
>
@@ -138,14 +138,14 @@ describe("flushSync", () => {
138138
fireEvent.click(screen.getByText("Go to /about"));
139139
await waitFor(() => screen.getByText("About"));
140140
expect(spy).toBeCalledTimes(2);
141-
expect(spy.mock.calls[0][1].unstable_flushSync).toBe(false);
142-
expect(spy.mock.calls[1][1].unstable_flushSync).toBe(false);
141+
expect(spy.mock.calls[0][1].flushSync).toBe(false);
142+
expect(spy.mock.calls[1][1].flushSync).toBe(false);
143143

144144
fireEvent.click(screen.getByText("Go to /"));
145145
await waitFor(() => screen.getByText("Home"));
146146
expect(spy).toBeCalledTimes(4);
147-
expect(spy.mock.calls[2][1].unstable_flushSync).toBe(true);
148-
expect(spy.mock.calls[3][1].unstable_flushSync).toBe(false);
147+
expect(spy.mock.calls[2][1].flushSync).toBe(true);
148+
expect(spy.mock.calls[3][1].flushSync).toBe(false);
149149

150150
router.dispose();
151151
});
@@ -167,7 +167,7 @@ describe("flushSync", () => {
167167
<pre>{`async:${fetcher1.data}:${fetcher1.state}`}</pre>
168168
<button
169169
onClick={() =>
170-
fetcher2.load("/fetch", { unstable_flushSync: true })
170+
fetcher2.load("/fetch", { flushSync: true })
171171
}
172172
>
173173
Load sync
@@ -202,14 +202,14 @@ describe("flushSync", () => {
202202
fireEvent.click(screen.getByText("Load async"));
203203
await waitFor(() => screen.getByText("async:LOADER:idle"));
204204
expect(spy).toBeCalledTimes(2);
205-
expect(spy.mock.calls[0][1].unstable_flushSync).toBe(false);
206-
expect(spy.mock.calls[1][1].unstable_flushSync).toBe(false);
205+
expect(spy.mock.calls[0][1].flushSync).toBe(false);
206+
expect(spy.mock.calls[1][1].flushSync).toBe(false);
207207

208208
fireEvent.click(screen.getByText("Load sync"));
209209
await waitFor(() => screen.getByText("sync:LOADER:idle"));
210210
expect(spy).toBeCalledTimes(4);
211-
expect(spy.mock.calls[2][1].unstable_flushSync).toBe(true);
212-
expect(spy.mock.calls[3][1].unstable_flushSync).toBe(false);
211+
expect(spy.mock.calls[2][1].flushSync).toBe(true);
212+
expect(spy.mock.calls[3][1].flushSync).toBe(false);
213213

214214
router.dispose();
215215
});
@@ -238,7 +238,7 @@ describe("flushSync", () => {
238238
onClick={() =>
239239
fetcher2.submit(
240240
{},
241-
{ method: "post", action: "/", unstable_flushSync: true }
241+
{ method: "post", action: "/", flushSync: true }
242242
)
243243
}
244244
>
@@ -267,16 +267,16 @@ describe("flushSync", () => {
267267
fireEvent.click(screen.getByText("Submit async"));
268268
await waitFor(() => screen.getByText("async:ACTION:idle"));
269269
expect(spy).toBeCalledTimes(3);
270-
expect(spy.mock.calls[0][1].unstable_flushSync).toBe(false);
271-
expect(spy.mock.calls[1][1].unstable_flushSync).toBe(false);
272-
expect(spy.mock.calls[2][1].unstable_flushSync).toBe(false);
270+
expect(spy.mock.calls[0][1].flushSync).toBe(false);
271+
expect(spy.mock.calls[1][1].flushSync).toBe(false);
272+
expect(spy.mock.calls[2][1].flushSync).toBe(false);
273273

274274
fireEvent.click(screen.getByText("Submit sync"));
275275
await waitFor(() => screen.getByText("sync:ACTION:idle"));
276276
expect(spy).toBeCalledTimes(6);
277-
expect(spy.mock.calls[3][1].unstable_flushSync).toBe(true);
278-
expect(spy.mock.calls[4][1].unstable_flushSync).toBe(false);
279-
expect(spy.mock.calls[5][1].unstable_flushSync).toBe(false);
277+
expect(spy.mock.calls[3][1].flushSync).toBe(true);
278+
expect(spy.mock.calls[4][1].flushSync).toBe(false);
279+
expect(spy.mock.calls[5][1].flushSync).toBe(false);
280280

281281
router.dispose();
282282
});

‎packages/react-router-dom/dom.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ interface SharedSubmitOptions {
188188
/**
189189
* Enable flushSync for this submission's state updates
190190
*/
191-
unstable_flushSync?: boolean;
191+
flushSync?: boolean;
192192
}
193193

194194
/**
@@ -225,7 +225,7 @@ export interface SubmitOptions extends FetcherSubmitOptions {
225225
/**
226226
* Enable view transitions on this submission navigation
227227
*/
228-
unstable_viewTransition?: boolean;
228+
viewTransition?: boolean;
229229
}
230230

231231
const supportedFormEncTypes: Set<FormEncType> = new Set([

0 commit comments

Comments
 (0)