Skip to content

Commit 830eddb

Browse files
committed
Merge branch 'main' into release-next
2 parents bc6fefa + 230d9e5 commit 830eddb

File tree

10 files changed

+150
-115
lines changed

10 files changed

+150
-115
lines changed

contributors.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Ajayff4
99
- akamfoad
1010
- alany411
11+
- alberto
1112
- alexlbr
1213
- AmRo045
1314
- amsal
@@ -79,6 +80,7 @@
7980
- Isammoc
8081
- ivanjeremic
8182
- ivanjonas
83+
- JackPriceBurns
8284
- jacob-ebey
8385
- JaffParker
8486
- jakkku
@@ -104,6 +106,7 @@
104106
- koojaa
105107
- KostiantynPopovych
106108
- KutnerUri
109+
- landisdesign
107110
- latin-1
108111
- lequangdongg
109112
- liuhanqu
@@ -126,15 +129,18 @@
126129
- matt-harro
127130
- maxpou
128131
- mcansh
132+
- MeatSim
129133
- MenouerBetty
130134
- mfijas
131135
- MichaelDeBoey
132136
- michal-antczak
133137
- minami-minami
138+
- minthulim
134139
- modex98
135140
- morleytatro
136141
- ms10596
137142
- ned-park
143+
- nilubisan
138144
- noisypigeon
139145
- Obi-Dann
140146
- omar-moquete
@@ -155,6 +161,7 @@
155161
- sanketshah19
156162
- senseibarni
157163
- sergiodxa
164+
- sgalhs
158165
- shamsup
159166
- shihanng
160167
- shivamsinghchahar

docs/components/nav-link.md

Lines changed: 94 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -4,134 +4,120 @@ title: NavLink
44

55
# `<NavLink>`
66

7-
<details>
8-
<summary>Type declaration</summary>
7+
A `<NavLink>` is a special kind of `<Link>` that knows whether or not it is "active" or "pending". This is useful when building a navigation menu, such as a breadcrumb or a set of tabs where you'd like to show which of them is currently selected. It also provides useful context for assistive technology like screen readers.
98

109
```tsx
11-
declare function NavLink(
12-
props: NavLinkProps
13-
): React.ReactElement;
14-
15-
interface NavLinkProps
16-
extends Omit<
17-
LinkProps,
18-
"className" | "style" | "children"
19-
> {
20-
caseSensitive?: boolean;
21-
children?:
22-
| React.ReactNode
23-
| ((props: { isActive: boolean }) => React.ReactNode);
24-
className?:
25-
| string
26-
| ((props: {
27-
isActive: boolean;
28-
}) => string | undefined);
29-
end?: boolean;
30-
style?:
31-
| React.CSSProperties
32-
| ((props: {
33-
isActive: boolean;
34-
}) => React.CSSProperties);
35-
}
36-
```
10+
import { NavLink } from "react-router-dom";
3711

38-
</details>
12+
<NavLink
13+
to="/messages"
14+
className={({ isActive, isPending }) =>
15+
isPending ? "pending" : isActive ? "active" : ""
16+
}
17+
>
18+
Messages
19+
</NavLink>;
20+
```
3921

40-
A `<NavLink>` is a special kind of [`<Link>`][link] that knows whether or not it is "active". This is useful when building a navigation menu such as a breadcrumb or a set of tabs where you'd like to show which of them is currently selected. It also provides useful context for assistive technology like screen readers.
22+
## Default `active` class
4123

42-
By default, an `active` class is added to a `<NavLink>` component when it is active. This provides the same simple styling mechanism for most users who are upgrading from v5. One difference as of `v6.0.0-beta.3` is that `activeClassName` and `activeStyle` have been removed from `NavLinkProps`. Instead, you can pass a function to either `style` or `className` that will allow you to customize the inline styling or the class string based on the component's active state. You can also pass a function as children to customize the content of the `<NavLink>` component based on their active state, specially useful to change styles on internal elements.
24+
By default, an `active` class is added to a `<NavLink>` component when it is active so you can use CSS to style it.
4325

4426
```tsx
45-
import * as React from "react";
46-
import { NavLink } from "react-router-dom";
27+
<nav id="sidebar">
28+
<NavLink to="/messages" />
29+
</nav>
30+
```
4731

48-
function NavList() {
49-
// This styling will be applied to a <NavLink> when the
50-
// route that it links to is currently selected.
51-
let activeStyle = {
52-
textDecoration: "underline",
53-
};
54-
55-
let activeClassName = "underline";
56-
57-
return (
58-
<nav>
59-
<ul>
60-
<li>
61-
<NavLink
62-
to="messages"
63-
style={({ isActive }) =>
64-
isActive ? activeStyle : undefined
65-
}
66-
>
67-
Messages
68-
</NavLink>
69-
</li>
70-
<li>
71-
<NavLink
72-
to="tasks"
73-
className={({ isActive }) =>
74-
isActive ? activeClassName : undefined
75-
}
76-
>
77-
Tasks
78-
</NavLink>
79-
</li>
80-
<li>
81-
<NavLink to="tasks">
82-
{({ isActive }) => (
83-
<span
84-
className={
85-
isActive ? activeClassName : undefined
86-
}
87-
>
88-
Tasks
89-
</span>
90-
)}
91-
</NavLink>
92-
</li>
93-
</ul>
94-
</nav>
95-
);
32+
```css
33+
#sidebar a.active {
34+
color: red;
9635
}
9736
```
9837

99-
If you prefer the v5 API, you can create your own `<NavLink />` as a wrapper component:
38+
## `className`
39+
40+
The `className` prop works like a normal className, but you can also pass it a function to customize the classNames applied based on the active and pending state of the link.
10041

10142
```tsx
102-
import * as React from "react";
103-
import { NavLink as BaseNavLink } from "react-router-dom";
104-
105-
const NavLink = React.forwardRef(
106-
({ activeClassName, activeStyle, ...props }, ref) => {
107-
return (
108-
<BaseNavLink
109-
ref={ref}
110-
{...props}
111-
className={({ isActive }) =>
112-
[
113-
props.className,
114-
isActive ? activeClassName : null,
115-
]
116-
.filter(Boolean)
117-
.join(" ")
118-
}
119-
style={({ isActive }) => ({
120-
...props.style,
121-
...(isActive ? activeStyle : null),
122-
})}
123-
/>
124-
);
43+
<NavLink
44+
to="/messages"
45+
className={({ isActive, isPending }) =>
46+
isPending ? "pending" : isActive ? "active" : ""
12547
}
126-
);
48+
>
49+
Messages
50+
</NavLink>
51+
```
52+
53+
## `style`
54+
55+
The `style` prop works like a normal style prop, but you can also pass it a function to customize the styles applied based on the active and pending state of the link.
56+
57+
```tsx
58+
<NavLink
59+
to="/messages"
60+
style={({ isActive, isPending }) => {
61+
return {
62+
fontWeight: isActive ? "bold" : "",
63+
color: isPending ? "red" : "black",
64+
};
65+
}}
66+
>
67+
Messages
68+
</NavLink>
69+
```
70+
71+
## `children`
72+
73+
You can pass a render prop as children to customize the content of the `<NavLink>` based on the active and pending state, which is useful to change styles on internal elements.
74+
75+
```tsx
76+
<NavLink to="/tasks">
77+
{({ isActive, isPending }) => (
78+
<span className={isActive ? "active" : ""}>Tasks</span>
79+
)}
80+
</NavLink>
12781
```
12882

129-
If the `end` prop is used, it will ensure this component isn't matched as "active" when its descendant paths are matched. For example, to render a link that is only active at the website root and not any other URLs, you can use:
83+
## `end`
84+
85+
The `end` prop changes the matching logic for the `active` and `pending` states to only match to the "end" of the NavLinks's `to` path. If the URL is longer than `to`, it will no longer be considered active.
86+
87+
Without the end prop, this link is always active because every URL matches `/`.
88+
89+
```tsx
90+
<NavLink to="/">Home</NavLink>
91+
```
92+
93+
To match the URL "to the end" of `to`, use `end`:
13094

13195
```tsx
13296
<NavLink to="/" end>
13397
Home
13498
</NavLink>
13599
```
136100

137-
[link]: ./link
101+
Now this link will only be active at `"/"`. This works for paths with more segments as well:
102+
103+
| Link | URL | isActive |
104+
| ----------------------------- | ------------ | -------- |
105+
| `<NavLink to="/tasks" />` | `/tasks` | true |
106+
| `<NavLink to="/tasks" />` | `/tasks/123` | true |
107+
| `<NavLink to="/tasks" end />` | `/tasks` | true |
108+
| `<NavLink to="/tasks" end />` | `/tasks/123` | false |
109+
110+
## `caseSensitive`
111+
112+
Adding the `caseSensitive` prop changes the matching logic to make it case sensitive.
113+
114+
| Link | URL | isActive |
115+
| -------------------------------------------- | ------------- | -------- |
116+
| `<NavLink to="/SpOnGe-bOB" />` | `/sponge-bob` | true |
117+
| `<NavLink to="/SpOnGe-bOB" caseSensitive />` | `/sponge-bob` | false |
118+
119+
## `aria-current`
120+
121+
When a `NavLink` is active it will automatically apply `<a aria-current="page">` to the underlying anchor tag. See [aria-current][aria-current] on MDN.
122+
123+
[aria-current]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current

docs/guides/deferred.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,43 @@ When you decide you'd like to try the trade-offs of `defer`, we don't want you t
207207
208208
So just keep this in mind: **Deferred is 100% only about the initial load of a route and its params.**
209209
210+
### Why don't Response objects returned by the loader work anymore?
211+
212+
When you use `defer`, you're telling React Router to load the page immediately, without the deferred data. The page is already loaded before the `Response` object is returned so responses are not automatically processed in the same way as if you had done `return fetch(url)`.
213+
214+
Therefore, you will need to handle your own `Response` processing and resolve your deferred Promise with data, not a `Response` instance.
215+
216+
```jsx
217+
async function loader({ request, params }) {
218+
return defer({
219+
// Broken! Resolves with a Response
220+
// broken: fetch(url),
221+
222+
// Fixed! Resolves with the response data
223+
data: fetch(url).then((res) => res.json()),
224+
});
225+
}
226+
```
227+
228+
Or consider the scenario where our deferred data could return a redirect `Response`. You can detect the redirect and send the status code and location back as data, and then you could perform a client-side redirect in your component via `useEffect` and `useNavigate`.
229+
230+
```jsx
231+
async function loader({ request, params }) {
232+
let data = fetch(url).then((res) => {
233+
if (res.status == 301) {
234+
return {
235+
isRedirect: true,
236+
status: res.status,
237+
location: res.headers.get("Location"),
238+
};
239+
}
240+
return res.json();
241+
});
242+
243+
return defer({ data });
244+
}
245+
```
246+
210247
[link]: ../components/link
211248
[usefetcher]: ../hooks/use-fetcher
212249
[defer response]: ../utils/defer

docs/hooks/use-submit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ new: true
55

66
# `useSubmit`
77

8-
The imperative version of `<Form>` that let's you, the programmer, submit a form instead of the user.
8+
The imperative version of `<Form>` that lets you, the programmer, submit a form instead of the user.
99

1010
<docs-warning>This feature only works if using a data router, see [Picking a Router][pickingarouter]</docs-warning>
1111

docs/routers/create-memory-router.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ new: true
55

66
# `createMemoryRouter`
77

8-
Instead of using the browsers history a memory router manages it's own history stack in memory. It's primarily useful for testing and component development tools like Storybook, but can also be used for running React Router in any non-browser environment.
8+
Instead of using the browser's history, a memory router manages its own history stack in memory. It's primarily useful for testing and component development tools like Storybook, but can also be used for running React Router in any non-browser environment.
99

1010
```jsx lines=[2-3,24-27]
1111
import {

docs/start/concepts.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,10 @@ And the resulting element tree rendered will be:
664664
</PageLayout>
665665
```
666666

667+
<docs-warning>
668+
Don't forget to add an `<Outlet>` to your layout where you would like child route elements to be rendered. Using `children` will not work as expected.
669+
</docs-warning>
670+
667671
The `PageLayout` route is admittedly weird. We call it a [layout route](#layout-route) because it doesn't participate in the matching at all (though its children do). It only exists to make wrapping multiple child routes in the same layout simpler. If we didn't allow this then you'd have to handle layouts in two different ways: sometimes your routes do it for you, sometimes you do it manually with lots of layout component repetition throughout your app:
668672

669673
<docs-error>You can do it like this, but we recommend using a layout route</docs-error>

docs/start/tutorial.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ These params are most often used to find a record by ID. Let's try it out.
694694

695695
👉 **Add a loader to the contact page and access data with `useLoaderData`**
696696

697-
```jsx filename=src/routes/contact.jsx lines=[1,2,4-6,9]
697+
```jsx filename=src/routes/contact.jsx lines=[1,2,4-6,10]
698698
import { Form, useLoaderData } from "react-router-dom";
699699
import { getContact } from "../contacts";
700700

@@ -1790,7 +1790,7 @@ There is one key difference though, it's not a navigation--the URL doesn't chang
17901790

17911791
## Optimistic UI
17921792

1793-
You probably noticed the app felt kind of unresponsive when we clicked the the favorite button from the last section. Once again, we added some network latency because you're going to have it in the real world!
1793+
You probably noticed the app felt kind of unresponsive when we clicked the favorite button from the last section. Once again, we added some network latency because you're going to have it in the real world!
17941794

17951795
To give the user some feedback, we could put the star into a loading state with [`fetcher.state`][fetcherstate] (a lot like `navigation.state` from before), but we can do something even better this time. We can use a strategy called "optimistic UI"
17961796

examples/auth/src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { BrowserRouter } from "react-router-dom";
55
import "./index.css";
66
import App from "./App";
77

8-
ReactDOM.createRoot(document.getElementById("root")).render(
8+
ReactDOM.createRoot(document.getElementById("root")!).render(
99
<React.StrictMode>
1010
<BrowserRouter>
1111
<App />

examples/custom-filter-link/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
useParams,
99
} from "react-router-dom";
1010
import type { LinkProps } from "react-router-dom";
11-
import VisuallyHidden from "@reach/visually-hidden";
11+
import { VisuallyHidden } from "@reach/visually-hidden";
1212

1313
import { brands, filterByBrand, getSneakerById, SNEAKERS } from "./snkrs";
1414

examples/multi-app/server.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ async function createServer() {
1414
if (!isProduction) {
1515
vite = await require("vite").createServer({
1616
root: process.cwd(),
17-
server: { middlewareMode: "ssr" },
17+
server: { middlewareMode: true },
18+
appType: "custom",
1819
});
1920

2021
app.use(vite.middlewares);

0 commit comments

Comments
 (0)