Skip to content

Commit

Permalink
BREAKING CHANGE: Enable newNextLinkBehavior (#41459)
Browse files Browse the repository at this point in the history
- Enable newNextLinkBehavior. See #36436 
- Run next/link codemod on test suite

Note that from when this lands on apps trying canary will need to run
the new-link codemod in order to upgrade.
Ideally we have to detect `<a>` while rendering the new link and warn
for it.

Co-authored-by: Steven <steven@ceriously.com>
  • Loading branch information
timneutkens and styfle committed Oct 18, 2022
1 parent f5cb7bd commit f260328
Show file tree
Hide file tree
Showing 365 changed files with 1,878 additions and 1,805 deletions.
24 changes: 24 additions & 0 deletions docs/advanced-features/codemods.md
Expand Up @@ -19,6 +19,30 @@ Codemods are transformations that run on your codebase programmatically. This al

## Next.js 13

### `new-link`

Safely removes `<a>` from `next/link` or adds `legacyBehavior` prop.

For example:

```jsx
export default function Page() {
return (
<Link href="/about">
<a>About Us</a>
</Link>
)
}
```

Transforms into:

```jsx
export default function Page() {
return <Link href="/about">About Us</Link>
}
```

### `next-image-to-legacy-image`

Safely migrates existing Next.js 10, 11, 12 applications importing `next/image` to the renamed `next/legacy/image` import in Next.js 13.
Expand Down
4 changes: 2 additions & 2 deletions docs/advanced-features/i18n-routing.md
Expand Up @@ -233,7 +233,7 @@ import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/another" locale="fr">
<a>To /fr/another</a>
To /fr/another
</Link>
)
}
Expand Down Expand Up @@ -279,7 +279,7 @@ import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/fr/another" locale={false}>
<a>To /fr/another</a>
To /fr/another
</Link>
)
}
Expand Down
4 changes: 1 addition & 3 deletions docs/api-reference/next.config.js/basepath.md
Expand Up @@ -35,9 +35,7 @@ For example, using `/about` will automatically become `/docs/about` when `basePa
export default function HomePage() {
return (
<>
<Link href="/about">
<a>About Page</a>
</Link>
<Link href="/about">About Page</Link>
</>
)
}
Expand Down
46 changes: 28 additions & 18 deletions docs/api-reference/next/link.md
Expand Up @@ -31,19 +31,13 @@ function Home() {
return (
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">
<a>About Us</a>
</Link>
<Link href="/about">About Us</Link>
</li>
<li>
<Link href="/blog/hello-world">
<a>Blog Post</a>
</Link>
<Link href="/blog/hello-world">Blog Post</Link>
</li>
</ul>
)
Expand All @@ -56,6 +50,7 @@ export default Home

- `href` - The path or URL to navigate to. This is the only required prop. It can also be an object, see [example here](/docs/api-reference/next/link.md#with-url-object)
- `as` - Optional decorator for the path that will be shown in the browser URL bar. Before Next.js 9.5.3 this was used for dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked. Note: when this path differs from the one provided in `href` the previous `href`/`as` behavior is used as shown in the [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes).
- [`legacyBehavior`](#if-the-child-is-a-tag) - Changes behavior so that child must be `<a>`. Defaults to `false`.
- [`passHref`](#if-the-child-is-a-custom-component-that-wraps-an-a-tag) - Forces `Link` to send the `href` property to its child. Defaults to `false`
- `prefetch` - Prefetch the page in the background. Defaults to `true`. Any `<Link />` that is in the viewport (initially or through scroll) will be preloaded. Prefetch can be disabled by passing `prefetch={false}`. When `prefetch` is set to `false`, prefetching will still occur on hover. Pages using [Static Generation](/docs/basic-features/data-fetching/get-static-props.md) will preload `JSON` files with the data for faster page transitions. Prefetching is only enabled in production.
- [`replace`](#replace-the-url-instead-of-push) - Replace the current `history` state instead of adding a new url into the stack. Defaults to `false`
Expand All @@ -78,7 +73,7 @@ function Posts({ posts }) {
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${encodeURIComponent(post.slug)}`}>
<a>{post.title}</a>
{post.title}
</Link>
</li>
))}
Expand All @@ -89,6 +84,22 @@ function Posts({ posts }) {
export default Posts
```

## If the child is `<a>` tag

```jsx
import Link from 'next/link'

function Legacy() {
return (
<Link href="/about" legacyBehavior>
<a>About Us</a>
</Link>
)
}

export default Legacy
```

## If the child is a custom component that wraps an `<a>` tag

If the child of `Link` is a custom component that wraps an `<a>` tag, you must add `passHref` to `Link`. This is necessary if you’re using libraries like [styled-components](https://styled-components.com/). Without this, the `<a>` tag will not have the `href` attribute, which hurts your site's accessibility and might affect SEO. If you're using [ESLint](/docs/basic-features/eslint.md#eslint-plugin), there is a built-in rule `next/link-passhref` to ensure correct usage of `passHref`.
Expand All @@ -103,9 +114,8 @@ const RedLink = styled.a`
`

function NavLink({ href, name }) {
// Must add passHref to Link
return (
<Link href={href} passHref>
<Link href={href} passHref legacyBehavior>
<RedLink>{name}</RedLink>
</Link>
)
Expand All @@ -119,7 +129,7 @@ export default NavLink

## If the child is a functional component

If the child of `Link` is a functional component, in addition to using `passHref`, you must wrap the component in [`React.forwardRef`](https://reactjs.org/docs/react-api.html#reactforwardref):
If the child of `Link` is a functional component, in addition to using `passHref` and `legacyBehavior`, you must wrap the component in [`React.forwardRef`](https://reactjs.org/docs/react-api.html#reactforwardref):

```jsx
import Link from 'next/link'
Expand All @@ -136,7 +146,7 @@ const MyButton = React.forwardRef(({ onClick, href }, ref) => {

function Home() {
return (
<Link href="/about" passHref>
<Link href="/about" passHref legacyBehavior>
<MyButton />
</Link>
)
Expand All @@ -162,7 +172,7 @@ function Home() {
query: { name: 'test' },
}}
>
<a>About us</a>
About us
</Link>
</li>
<li>
Expand All @@ -172,7 +182,7 @@ function Home() {
query: { slug: 'my-post' },
}}
>
<a>Blog Post</a>
Blog Post
</Link>
</li>
</ul>
Expand All @@ -195,7 +205,7 @@ The default behavior of the `Link` component is to `push` a new URL into the `hi

```jsx
<Link href="/about" replace>
<a>About us</a>
About us
</Link>
```

Expand All @@ -205,6 +215,6 @@ The default behavior of `Link` is to scroll to the top of the page. When there i

```jsx
<Link href="/#hashid" scroll={false}>
<a>Disables scrolling to the top</a>
Disables scrolling to the top
</Link>
```
6 changes: 1 addition & 5 deletions docs/api-reference/next/router.md
Expand Up @@ -156,11 +156,7 @@ export default function Page(props) {
<h1>Page: {router.query.slug}</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increase count</button>
<Link href="/one">
<a>one</a>
</Link> <Link href="/two">
<a>two</a>
</Link>
<Link href="/one">one</Link> <Link href="/two">two</Link>
</div>
)
}
Expand Down
8 changes: 2 additions & 6 deletions docs/migrating/from-gatsby.md
Expand Up @@ -78,15 +78,11 @@ export default function Home() {
import Link from 'next/link'

export default function Home() {
return (
<Link href="/blog">
<a>blog</a>
</Link>
)
return <Link href="/blog">blog</Link>
}
```

Update any import statements, switch `to` to `href`, and add an `<a>` tag as a child of the element.
Update any import statements, switch `to` to `href`.

## Data Fetching

Expand Down
2 changes: 1 addition & 1 deletion docs/migrating/from-react-router.md
Expand Up @@ -34,7 +34,7 @@ import Link from 'next/link'
export default function App() {
return (
<Link href="/about">
<a>About</a>
About
</Link>
)
}
Expand Down
10 changes: 3 additions & 7 deletions docs/routing/dynamic-routes.md
Expand Up @@ -63,18 +63,14 @@ function Home() {
return (
<ul>
<li>
<Link href="/post/abc">
<a>Go to pages/post/[pid].js</a>
</Link>
<Link href="/post/abc">Go to pages/post/[pid].js</Link>
</li>
<li>
<Link href="/post/abc?foo=bar">
<a>Also goes to pages/post/[pid].js</a>
</Link>
<Link href="/post/abc?foo=bar">Also goes to pages/post/[pid].js</Link>
</li>
<li>
<Link href="/post/abc/a-comment">
<a>Go to pages/post/[pid]/[comment].js</a>
Go to pages/post/[pid]/[comment].js
</Link>
</li>
</ul>
Expand Down
16 changes: 5 additions & 11 deletions docs/routing/introduction.md
Expand Up @@ -47,19 +47,13 @@ function Home() {
return (
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">
<a>About Us</a>
</Link>
<Link href="/about">About Us</Link>
</li>
<li>
<Link href="/blog/hello-world">
<a>Blog Post</a>
</Link>
<Link href="/blog/hello-world">Blog Post</Link>
</li>
</ul>
)
Expand Down Expand Up @@ -89,7 +83,7 @@ function Posts({ posts }) {
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${encodeURIComponent(post.slug)}`}>
<a>{post.title}</a>
{post.title}
</Link>
</li>
))}
Expand Down Expand Up @@ -118,7 +112,7 @@ function Posts({ posts }) {
query: { slug: post.slug },
}}
>
<a>{post.title}</a>
{post.title}
</Link>
</li>
))}
Expand Down
8 changes: 2 additions & 6 deletions docs/testing.md
Expand Up @@ -66,9 +66,7 @@ import Link from 'next/link'
export default function Home() {
return (
<nav>
<Link href="/about">
<a>About</a>
</Link>
<Link href="/about">About</Link>
</nav>
)
}
Expand Down Expand Up @@ -183,9 +181,7 @@ import Link from 'next/link'
export default function Home() {
return (
<nav>
<Link href="/about">
<a>About</a>
</Link>
<Link href="/about">About</Link>
</nav>
)
}
Expand Down
2 changes: 2 additions & 0 deletions docs/upgrading.md
Expand Up @@ -11,6 +11,8 @@ The minimum Node.js version has been bumped from 12.22.0 to 14.0.0, since 12.x h
The `next/image` import was renamed to `next/legacy/image`. The `next/future/image` import was renamed to `next/image`.
A [codemod is available](/docs/advanced-features/codemods.md#next-image-to-legacy-image) to safely and automatically rename your imports.

The `next/link` child can no longer be `<a>`. Add the `legacyBehavior` prop to use the legacy behavior or remove the `<a>` to upgrade. A [codemod is available](/docs/advanced-features/codemods.md#new-link) to automatically upgrade your code.

## Upgrading to 12.2

If you were using Middleware prior to `12.2`, please see the [upgrade guide](https://nextjs.org/docs/messages/middleware-upgrade-guide) for more information.
Expand Down
2 changes: 1 addition & 1 deletion packages/next/build/jest/jest.ts
@@ -1,7 +1,7 @@
import { loadEnvConfig } from '@next/env'
import { resolve, join } from 'path'
import loadConfig from '../../server/config'
import { NextConfigComplete } from '../../server/config-shared'
import type { NextConfigComplete } from '../../server/config-shared'
import { PHASE_TEST } from '../../shared/lib/constants'
import loadJsConfig from '../load-jsconfig'
import * as Log from '../output/log'
Expand Down
3 changes: 1 addition & 2 deletions packages/next/server/config-shared.ts
Expand Up @@ -556,8 +556,7 @@ export const defaultConfig: NextConfig = {
// TODO: change default in next major release (current v12.1.5)
legacyBrowsers: true,
browsersListForSwc: false,
// TODO: change default in next major release (current v12.1.5)
newNextLinkBehavior: false,
newNextLinkBehavior: true,
cpus: Math.max(
1,
(Number(process.env.CIRCLE_NODE_TOTAL) ||
Expand Down
4 changes: 2 additions & 2 deletions test/development/basic-basepath/hmr/pages/hmr/index.js
Expand Up @@ -2,8 +2,8 @@ import Link from 'next/link'

export default () => (
<div>
<Link href="/hmr/error-in-gip">
<a id="error-in-gip-link">Bad Page</a>
<Link href="/hmr/error-in-gip" id="error-in-gip-link">
Bad Page
</Link>
</div>
)
Expand Up @@ -4,7 +4,7 @@ export default function IndexPage() {
return (
<div>
<Link href="/about" prefetch>
<a>To About Page</a>
To About Page
</Link>
</div>
)
Expand Down
Expand Up @@ -3,9 +3,7 @@ import Link from 'next/link'
export default function NoPrefetchPage() {
return (
<div>
<Link href="/about">
<a>No prefetch</a>
</Link>
<Link href="/about">No prefetch</Link>
</div>
)
}
Expand Up @@ -4,7 +4,7 @@ export default function PrefetchFalsePage() {
return (
<div>
<Link href="/about" prefetch={false}>
<a>Prefetch set to false</a>
Prefetch set to false
</Link>
</div>
)
Expand Down
Expand Up @@ -2,8 +2,6 @@ import Link from 'next/link'

export default () => (
<div>
<Link href="/dynamic/no-chunk">
<a>No Chunk</a>
</Link>
<Link href="/dynamic/no-chunk">No Chunk</Link>
</div>
)

0 comments on commit f260328

Please sign in to comment.