vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.83k stars 26.96k forks source link

Using `getStaticPaths` in combination with `trailingSlash: true` leads to invalid json prefetching #17813

Closed bram-l closed 3 years ago

bram-l commented 4 years ago

Bug report

Using getStaticPaths in combination with trailingSlash: true leads to invalid json prefetching. For a link to a dynamic route, the json file that is prefetched includes an invalid slash in the path, causing a 404 to be returned. However, when clicking the link and landing on the dynamic page the correct json file is fetched.

To Reproduce

  1. Configure
// next.config.js
module.exports = {
  trailingSlash: true,
};
  1. Add dynamic route
// pages/[name].js
export default function Hello({ name }) {
  return <div>Hello {name}</div>
}

export function getStaticProps(context) {
  return {
    props: {
      name: context.params.name
    }
  };
}

export function getStaticPaths() {
  return {
    paths: [
      { params: { name: 'world' } }
    ],
    fallback: false
  };
}
  1. Add link to dynamic route
// pages/index.js
import Link from 'next/link'
export default function Home() {
  return (
    <div>
      <Link as="/world" href="/[name]">
        <a>Hello world</a>
      </Link>
    </div>
  )
}
  1. Export using next build && next export
  2. Open root page
  3. Observe a network request for https://example.com/_next/data/some-hash/world/.json returning a 404

Expected behavior

The correct https://example.com/_next/data/some-hash/world.json is (pre)fetched

System information

erkde commented 4 years ago

I've had a little look at this (it's also breaking the prefetching in the app I'm currently working on), I have pushed a little demo app here to reproduce the issue.

That app is now versioned to 9.5.4-canary.4, the earliest version where I see it happening, 9.5.3-canary.3 or earlier it seems fine, so I'm guessing it's related to this commit 489cad36bcc95f93ce012712369a83809e91956d that was introduced between those two versions.

Adding a little debugging, before this commit, the function _resolveHref was returning

{
  href: "/[name]/"
  pathname: "/[name]"
  ...
}

but after it returns:

{
  href: "/[name]/"
  pathname: "/[name]/"
}

and further along gets translated to '/world/' and then has the extension appended to it, creating '/world/.json'.

Just as a test, I've tried sanitising the URL just before calling prefetchData, it seems to resolve this issue, but not so confident it doesn't introduce others

git diff
diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts
index 85921f4d6..268b86920 100644
--- a/packages/next/next-server/lib/router/router.ts
+++ b/packages/next/next-server/lib/router/router.ts
@@ -1146,6 +1146,8 @@ export default class Router implements BaseRouter {
     }

     const route = removePathTrailingSlash(pathname)
+    url = removePathTrailingSlash(url)
+
     await Promise.all([
       this.pageLoader.prefetchData(
         url,
bernardodestefano commented 4 years ago

I have exactly the same issue, any updates?

emadicio commented 4 years ago

Following on this. It is a really serious issue for our project, as not being able to fetch the pages .json breaks prefetching of links, causing the entire app to re-render when you click on a link and subsequent loss of data in React contexts.

erkde commented 4 years ago

As a temporary workaround, maybe just turn it off with prefetch={false} on the affected Links.

jchen1 commented 4 years ago

What worked for me is disabling prefetch in my Links and prefetching with router.prefetch in a Hook instead:

function LinkWrapper({ slug, text }) {
  const router = useRouter();

  useEffect(() => {
    router.prefetch(`/posts/${encodeURIComponent(slug)}`);
  }, []);

  return (
    <Link href={`/posts/${encodeURIComponent(slug)}/`} prefetch={false}>
      <a>{text}</a>
    </Link>
  );
}
shawner18 commented 3 years ago

+1 I am also running into issues with this.

chaddjohnson commented 3 years ago

Definitely need a fix this this as disabling trailingSlash is a non-option. We started noticing this /.json issue when upgrading to Next.js 10. Could have started happening with 9.5.

mrrau commented 3 years ago

I have this error in 9.5.5 - shouldn't the fix also be merged to version 9?

balazsorban44 commented 2 years ago

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.