Skip to content

Commit cdab7bf

Browse files
kaykdmtimneutkenskodiakhq[bot]
authored
fix hydration mismatch on href for url with anchor refs (#21065)
Co-authored-by: Tim Neutkens <tim@timneutkens.nl> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent ceef156 commit cdab7bf

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

packages/next/next-server/lib/router/router.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ export function delBasePath(path: string): string {
163163
* Detects whether a given url is routable by the Next.js router (browser only).
164164
*/
165165
export function isLocalURL(url: string): boolean {
166-
if (url.startsWith('/')) return true
166+
// prevent a hydration mismatch on href for url with anchor refs
167+
if (url.startsWith('/') || url.startsWith('#')) return true
167168
try {
168169
// absolute urls can be local if they are on the same origin
169170
const locationOrigin = getLocationOrigin()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react'
2+
import Link from 'next/link'
3+
4+
const Home = () => {
5+
return (
6+
<>
7+
<Link href="#hash-link">
8+
<a>Hash Link</a>
9+
</Link>
10+
</>
11+
)
12+
}
13+
14+
export default Home
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* eslint-env jest */
2+
3+
import { join } from 'path'
4+
import webdriver from 'next-webdriver'
5+
import {
6+
findPort,
7+
launchApp,
8+
killApp,
9+
nextStart,
10+
nextBuild,
11+
} from 'next-test-utils'
12+
13+
jest.setTimeout(1000 * 60 * 5)
14+
let app
15+
let appPort
16+
const appDir = join(__dirname, '..')
17+
18+
const runTests = () => {
19+
it('should not have hydration mis-match for hash link', async () => {
20+
const browser = await webdriver(appPort, '/')
21+
const browserLogs = await browser.log('browser')
22+
let found = false
23+
browserLogs.forEach((log) => {
24+
if (log.message.includes('Warning: Prop')) {
25+
found = true
26+
}
27+
})
28+
expect(found).toEqual(false)
29+
})
30+
}
31+
32+
describe('Link with hash href', () => {
33+
describe('development', () => {
34+
beforeAll(async () => {
35+
appPort = await findPort()
36+
app = await launchApp(appDir, appPort)
37+
})
38+
afterAll(() => killApp(app))
39+
40+
runTests()
41+
})
42+
43+
describe('production', () => {
44+
beforeAll(async () => {
45+
await nextBuild(appDir)
46+
appPort = await findPort()
47+
app = await nextStart(appDir, appPort)
48+
})
49+
afterAll(() => killApp(app))
50+
51+
runTests()
52+
})
53+
})

0 commit comments

Comments
 (0)