rsnext/packages/next/server
Tim Neutkens 489e65ed98
Rework <Link> behavior (backwards compatible) (#36436)
Fixes https://github.com/vercel/next.js/discussions/32233

⚠️ If you're looking at this PR please read the complete description including the part about incremental adoption.

## TLDR:

Official support for `<Link href="/about">About</Link>` / `<Link href="/about"><CustomComponent /></Link>` / `<Link href="/about"><strong>About</strong></Link>` where `<Link>` always renders `<a>` without edge cases where it doesn’t render `<a>`. You'll no longer have to put an empty `<a>` in `<Link>` with this enabled.

## Full context

### Changes to `<Link>`

- Added an `legacyBehavior` prop that defaults to `true` to preserve the defaults we have today, this will allow to run a codemod on existing codebases to move them to the version where `legacyBehavior` becomes `false` by default
- When using the new behavior `<Link>` always renders an `<a>` instead of having `React.cloneElement` and passing props onto a child element
- When using the new behavior props that can be passed to `<a>` can be passed to `<Link>`. Previously you could do something like `<Link href="/somewhere"><a target="_blank">Download</a></Link>` but with `<Link>` rendering `<a>` it now allows these props to be set on link. E.g. `<Link href="/somewhere" target="_blank"></Link>` / `<Link href="/somewhere" className="link"></Link>`

### Incremental Adoption / Codemod

The main reason we haven't made these changes before is that it breaks pretty much all Next.js apps, which is why I've been hesitant to make this change in the past. I've spent a bunch of time figuring out what the right approach is to rolling this out and ended up with an approach that requires existing apps to run a codemod that automatically opts their `<Link>` usage into the old behavior in order to keep the app functioning.

This codemod will auto-fix the usage where possible. For example: 

- When you have `<Link href="/about"><a>About</a></Link>` it'll auto-fix to `<Link href="/about">About</Link>`
- When you have `<Link href="/about"><a onClick={() => console.log('clicked')}>About</a></Link>` it'll auto-fix to `<Link href="/about" onClick={() => console.log('clicked')}>About</Link>`
- For cases where auto-fixing can't be applied the `legacyBehavior` prop is added. When you have `<Link href="/about"><Component /></Link>` it'll transform to `<Link href="/about" legacyBehavior><Component /></Link>` so that your app keeps functioning using the old behavior for that particular link. It's then up to the dev to move that case out of the `legacyBehavior` prop.


**This default will be changed in Next.js 13, it does not affect existing apps in Next.js 12 unless opted in via `experimental.newLinkBehavior` and running the codemod.**

Some code samples of what changed:

```jsx
const CustomComponent = () => <strong>Hello</strong>

// Legacy behavior: `<a>` has to be nested otherwise it's excluded

// Renders: <a href="/about">About</a>. `<a>` has to be nested.
<Link href="/about">
  <a>About</a>  
</Link>

// Renders: <strong onClick={nextLinkClickHandler}>Hello</strong>. No `<a>` is included.
<Link href="/about">
  <strong>Hello</strong>
</Link>


// Renders: <strong onClick={nextLinkClickHandler}>Hello</strong>. No `<a>` is included.
<Link href="/about">
  <CustomComponent />
</Link>

// --------------------------------------------------
// New behavior: `<Link>` always renders `<a>`

// Renders: <a href="/about">About</a>. `<a>` no longer has to be nested.
<Link href="/about">
  About
</Link>

// Renders: <a href="/about"><strong>Hello</strong></a>. `<a>` is included.
<Link href="/about">
  <strong>Hello</strong>
</Link>

// Renders: <a href="/about"><strong>Hello</strong></a>. `<a>` is included.
<Link href="/about">
  <CustomComponent />
</Link>
```

---


## Feature

- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Errors have helpful link attached, see `contributing.md`


Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
2022-04-25 22:01:30 +00:00
..
api-utils Fix res.json support for string / null (#36186) 2022-04-15 16:04:00 +02:00
base-http Allow reading request bodies in middlewares (#34294) (#34519) 2022-02-18 19:43:43 +00:00
dev fix(next): use moduleGraph.getIssuer to avoid deprecation warning (#36329) (#36330) 2022-04-21 14:14:03 +00:00
lib Fix typos (#35683) 2022-03-28 22:53:51 -05:00
send-payload Reduce the size of web server (#34767) 2022-02-24 19:53:17 +00:00
web Do not polyfill node built-in modules on edge functions (#36190) 2022-04-25 09:25:27 +00:00
accept-header.ts Implement Middleware RFC (#30081) 2021-10-20 17:52:11 +00:00
base-server.ts Simplify the App and Component handling in render (#36395) 2022-04-22 21:03:57 +00:00
body-streams.ts fix post request hangs when no body is consumed on middleware (#35131) 2022-03-08 19:24:58 +00:00
config-shared.ts Rework <Link> behavior (backwards compatible) (#36436) 2022-04-25 22:01:30 +00:00
config-utils.ts Remove webpack 4 support (#29660) 2021-10-06 17:40:01 +02:00
config.ts Fix build activity indicator position (#36208) 2022-04-15 23:53:00 -05:00
crypto-utils.ts Move next-server directory files to server directory (#26756) 2021-06-30 13:44:40 +02:00
denormalize-page-path.ts Fix dynamic routes with pages under index folder (#32440) 2021-12-13 20:36:31 +00:00
font-utils.ts Move next-server directory files to server directory (#26756) 2021-06-30 13:44:40 +02:00
get-page-files.ts Move next-server directory files to server directory (#26756) 2021-06-30 13:44:40 +02:00
get-route-from-entrypoint.ts Move next-server directory files to server directory (#26756) 2021-06-30 13:44:40 +02:00
htmlescape.ts Upgrade to Prettier 2 (#13061) 2020-05-18 15:24:37 -04:00
image-optimizer.ts Update on-demand ISR to skip fetch locally (#35386) 2022-03-17 12:06:44 -05:00
incremental-cache.ts Use fallbackable path module for node and edge runtime (#36306) 2022-04-21 09:07:03 +00:00
load-components.ts Custom app for switchable runtime (#35666) 2022-04-05 19:46:17 +00:00
next-server.ts Update polyfill of web streams (#35571) 2022-04-07 16:26:30 +02:00
next.ts Base Http for BaseServer (#32999) 2022-01-14 21:01:35 +00:00
node-polyfill-fetch.js Pre-compile more dependencies (#32742) 2022-01-17 15:17:22 +00:00
node-polyfill-web-streams.js Update polyfill of web streams (#35571) 2022-04-07 16:26:30 +02:00
node-web-streams-helper.ts Use renderToStaticMarkup to render documentHTML (#36213) 2022-04-19 17:20:20 +00:00
normalize-page-path.ts Use fallbackable path module for node and edge runtime (#36306) 2022-04-21 09:07:03 +00:00
optimize-amp.ts Move next-server directory files to server directory (#26756) 2021-06-30 13:44:40 +02:00
render-result.ts Add unstable_useFlushEffects hook (#34117) 2022-02-18 00:18:28 +00:00
render.tsx Simplify the App and Component handling in render (#36395) 2022-04-22 21:03:57 +00:00
request-meta.ts Ensure we don't poll page in development when notFound: true is returned (#34352) 2022-02-16 18:53:48 +00:00
require.ts Expose WASM bindings in Middleware (#34437) 2022-03-02 15:09:36 +00:00
response-cache.ts Ensure minimalMode previousCache expire time is capped (#35954) 2022-04-07 11:32:45 -05:00
router-utils.ts test: warn on substr() usage (#35499) 2022-03-24 18:35:33 -04:00
router.ts Use finally to clean up seen requests (#36222) 2022-04-18 13:16:39 -05:00
serve-static.ts Add support for AVIF to next/image (#29683) 2021-10-11 23:17:47 +00:00
server-route-utils.ts Fix encoding error with location and refresh headers (#33763) 2022-01-28 16:20:34 +00:00
utils.ts Refactor base server to remove native dependencies (#33499) 2022-01-20 21:25:44 +00:00
web-server.ts Use fallbackable path module for node and edge runtime (#36306) 2022-04-21 09:07:03 +00:00