diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml index 7bc9dedbb4fd017..d6b3957f5a83b31 100644 --- a/.github/ISSUE_TEMPLATE/1.bug_report.yml +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -12,10 +12,10 @@ inputs: value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions "Help" section. - type: description attributes: - value: "Please first verify if your issue exists in the Next.js canary release line: `npm install next@canary`." + value: 'Please first verify if your issue exists in the Next.js canary release line: `npm install next@canary`.' - type: description attributes: - value: "next@canary is the beta version of Next.js. It includes all features and fixes that are pending to land on the stable release line." + value: 'next@canary is the beta version of Next.js. It includes all features and fixes that are pending to land on the stable release line.' - type: input attributes: label: What version of Next.js are you using? diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index e59fada6cbb1110..44cc9486e395912 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -163,7 +163,8 @@ export function delBasePath(path: string): string { * Detects whether a given url is routable by the Next.js router (browser only). */ export function isLocalURL(url: string): boolean { - if (url.startsWith('/')) return true + // prevent a hydration mismatch on href for url with anchor refs + if (url.startsWith('/') || url.startsWith('#')) return true try { // absolute urls can be local if they are on the same origin const locationOrigin = getLocationOrigin() diff --git a/test/integration/link-with-hash/pages/index.js b/test/integration/link-with-hash/pages/index.js new file mode 100644 index 000000000000000..f39a8ed7342d281 --- /dev/null +++ b/test/integration/link-with-hash/pages/index.js @@ -0,0 +1,14 @@ +import React from 'react' +import Link from 'next/link' + +const Home = () => { + return ( + <> + + Hash Link + + + ) +} + +export default Home diff --git a/test/integration/link-with-hash/test/index.test.js b/test/integration/link-with-hash/test/index.test.js new file mode 100644 index 000000000000000..365cade8ad9bbf0 --- /dev/null +++ b/test/integration/link-with-hash/test/index.test.js @@ -0,0 +1,53 @@ +/* eslint-env jest */ + +import { join } from 'path' +import webdriver from 'next-webdriver' +import { + findPort, + launchApp, + killApp, + nextStart, + nextBuild, +} from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 5) +let app +let appPort +const appDir = join(__dirname, '..') + +const runTests = () => { + it('should not have hydration mis-match for hash link', async () => { + const browser = await webdriver(appPort, '/') + const browserLogs = await browser.log('browser') + let found = false + browserLogs.forEach((log) => { + if (log.message.includes('Warning: Prop')) { + found = true + } + }) + expect(found).toEqual(false) + }) +} + +describe('Link with hash href', () => { + describe('development', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(() => killApp(app)) + + runTests() + }) + + describe('production', () => { + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + + runTests() + }) +})