diff --git a/packages/next/client/components/app-router.client.tsx b/packages/next/client/components/app-router.client.tsx index 41036c0e3ff7..a5ac17172dd5 100644 --- a/packages/next/client/components/app-router.client.tsx +++ b/packages/next/client/components/app-router.client.tsx @@ -82,7 +82,11 @@ export default function AppRouter({ }, pushRef: { pendingPush: false, mpaNavigation: false }, focusRef: { focus: false }, - canonicalUrl: initialCanonicalUrl, + canonicalUrl: + initialCanonicalUrl + + // Hash is read as the initial value for canonicalUrl in the browser + // This is safe to do as canonicalUrl can't be rendered, it's only used to control the history updates the useEffect further down. + (typeof window !== 'undefined' ? window.location.hash : ''), }) useEffect(() => { diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index c05c91f88f29..3541809f8150 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -1,6 +1,6 @@ import { createNext, FileRef } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' -import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils' +import { fetchViaHTTP, renderViaHTTP, waitFor } from 'next-test-utils' import path from 'path' import cheerio from 'cheerio' import webdriver from 'next-webdriver' @@ -243,6 +243,20 @@ describe('app dir', () => { } ) + it('should handle hash in initial url', async () => { + const browser = await webdriver(next.url, '/dashboard#abc') + + try { + // Check if hash is preserved + expect(await browser.eval('window.location.hash')).toBe('#abc') + await waitFor(1000) + // Check again to be sure as it might be timed different + expect(await browser.eval('window.location.hash')).toBe('#abc') + } finally { + await browser.close() + } + }) + describe('', () => { // TODO-APP: fix development test it.skip('should hard push', async () => {