Next.js: Custom route `post/`id` flashing 404 before finally rendering page.

Created on 22 Aug 2017  ·  47Comments  ·  Source: vercel/next.js

I'm currently experiencing a problem with my post/:id routes where next flashes the 404 page before successfully rendering my component. At first, I thought this was a redux related issues as my post/id pages are checking the app state to filter whichever component should be shown using shallow routing.

After discussing this in the slack channel, I added another API call that would retrieve each blog post but quickly found that I still encountered the same issue. I know that this has happened for at least one other developer so i'm wondering if anyone can point me in the right direction? I'm trying to finish up this personal portfolio I built with next.

Below, you can see exactly what's happening. Everything looks A1 except for that 404 that blinks whenever a specific post is rendered.

next.js blog post err

The benefits i've gained are tremendous however there have been some minor issues like this one that have got me a little puzzled 🤔 . Anyways, let me know if I can provide any additional info. Thanks 😄

Most helpful comment

So to answer this one once and for all:

href => path inside of the pages directory + the querystring
as => rendered in the browser url bar

Example:

You have a url called /products/:id

  1. You created pages/product.js
export default class extends React.Component {
  static async getInitialProps({query}) {
    console.log('SLUG', query.slug)
    return {}
  }
  render() {
    return <h1>The product page</h1>
  }
}
  1. You add the route to express or any other server, this is only for SSR. What this will do is route the url /products/:id to pages/product.js and provide id as part of query in getInitialProps:
server.get("/products/:slug", (req, res) => {
  return app.render(req, res, "/product", { slug: req.params.slug })
})
  1. For client side routing you use next/link like this:
<Link href="/product?slug=something" as="/products/something"> 

The reason you have to explicitly provide the href and as is that Next.js is not aware of any other pages on the client side. We don't send a manifest of all pages to the client side and subsequent routes get lazy-loaded, which is scalable.

There is a community package that abstracts away providing href and as by sending a pre-defined manifest of all routes, note that we recommend not sending a full manifest of all possible routes as not doing this is more scaleable

https://github.com/fridays/next-routes

Also, note that rendering 404 is no longer the default behavior of Next.js. Instead, we reload the page for the Zones feature.

All 47 comments

Same issue +1

How are you rendering the Link component to that url? Are you using <Link href="/post/1"> or <Link href="/post?id=1" as="/post/1">?

Currently, i'm using <Link prefetch href={/blog/${x.id}} />. I've tried <Link prefetch href={/blog/${x.id}} as={/blog/${x.id}} /> in the past and just plugged it in again. Still having the same issue.

Your href and as props shouldn't be the same, href is the real url, if you have pages/blog.js and receive id as a query then href should be /blog?id=${x.id} and as is your pretty custom url in this case /blog/${x.id}.

@sergiodxa I just put this code in place.

<Link href={`/blog?id=${x.id}`} as={`blog/${x.id}`}>
    <a>Read Post</a>
 </Link>

Now it's re-rendering the /blog page on every click while adding the id to the URL.

url error gif

@tgrecojs Hope this code snippet helps

    const href = `/journal?home=${this.state.article.path[0]}&articlePath=${this.state.article.path[1]}&file=${this.state.article.path[2]}`
    const as = `/journal/${this.state.article.path[0]}/${this.state.article.path[1]}/${this.state.article.path[2]}`

Would render as https://www.someURL.com/journal/articlePath/morePath/evenMorePath

I understand how the path's work @ugiacoman that's not my issue. If you can see in my initial report, i'm successfully navigating to each blog post however the issue is that weird 404 error.

@tgrecojs I made an example repo https://github.com/sergiodxa/next-custom-query (deployed: https://next-custom-query.now.sh/blog), there you can see how to use the custom urls without reloading and a 404 using the next/link component as described above.

@sergiodxa I appreciate that! I just checked it out and added some async calls to the google blogger API which is what i'm currently pinging. Below, you can see how it's navigating to the route but it's not correctly loading the data.

Removed gif

Now this issue is a slightly different reproduction of this issue than what is shown in my first comment. When I first ran into this problem, I thought it was because I was using redux and that the application state wasn't carrying over the post/id pages. This led me to integrating a new API call for each post, just like I integrated in the app you've created, however I still found that I had the same problem.

I was running into some trouble forking your repo 😬 so you can find my code here -> https://github.com/tgrecojs/next-custom-query-async. Let me know you're thoughts! 😄

It looks you're getting a 400, when forking your project I'm getting that same error with a usageLimits error message. That's probably why you're getting that error.

Ok. I'll investigate a little further tomorow but I want to point out that the video I posted when opening the issue is not making an API request on the /blog/:id route. Instead, it's filtering the posts from my redux store which is collected on the /blog route transition so that couldn't be the reason I arrived at this issue. I only implemented the 2nd API call (made inside blog/:id) as I thought it had something to do with my redux-store but found myself receiving the same error but that's not the current implementation in my code.

As soon as I realized that it threw the same bug, I switched back to using only one API call (Inside /blog) and then filtering for the necessary blog post by id.

@sergiodxa I can confirm that it's not usage limits. You'll see that the id isn't always being correctly passed to the /blog/:id route... I've attached the 400 error that i'm receiving. You'll see that id is undefined and (in this instance) it seems to be why i'm getting the error.

I've included another video below. Any thoughts as to why it may be behaving this way? I haven't changed the <Link /> tags inside this repository yet still having the issue 😬 .

next-custom-query err

Again, I want to point out that the video above shows my reproducing the error inside of the example repo you've created - https://github.com/sergiodxa/next-custom-query.

I'm also getting 404 flashing without any custom routes. Just a file called orders.js in pages/ with this code:

import { Component } from 'react';

class Orders extends Component {
  render() {
    return (
      <div>
        <h2>My Orders</h2>
      </div>
    )
  }
}

export default Orders;

Seems like a pretty critical issue 😅

I tried to replicate it and is because the trailing slash at the end of the URL, if you go to /orders you don't see the 404 but if you go to /orders/ then you see that 404 flashing before the content, this happens either in dev and production mode.

My issue didn't arise due to a trailing slash so there's got to be something else, right?

I've made some changes in my code and i've deployed 2 different versions, one using prefetch and one that doesn't. I want to note that neither application is now able to successfully load a post/:id route.

With Prefetch - https://tgrecojs-hltqztsjpx.now.sh/

One noticed that when I use prefetch in the link, it now throws a Page Does Not Exist Error when rendering the links for each blog post. You can see a screenshot of my console below.

screen shot 2017-08-26 at 1 06 25 pm

Without Prefetch- https://tgrecojs-qzpspdjrin.now.sh/

When I don't use prefetch, the error shown above no longer exists but it does not render the application. When I initially posted this issue, my component was initializing my blog for each post/:id component. I've since removed that since now individual posts never render.

Let me know if I can provide any more info that can help diagnose this issue. 🤔

@tgrecojs I have almost the exact same problem. prefetch did not solve my problem.

The behavior I get is the following:

  • The user clicks on the link
  • A 404 error shows up
  • The page reloads (??)
  • After reloading, the correct page is displayed

I hope this can help debug the issue!

Extra info: Node v6.10.3, Chrome Version 60.0.3112.90

EDIT: Deployed the app so you guys can test it https://datahub.now.sh/, it's the link /@[username] that has this behaviour

@Theo- the href in that next/link is user?id=1, can you try /user?id=1 instead? I think the 404 is because Next.js is trying to get to /current/path/user?id=1 instead of /user?id=1, but when the page reload it works fine because of the server render.

@sergiodxa that did it. Thank you!

@tgrecojs I had the same flashing 404 behavior. In my case, I had a link with an environment variable which didn't work on the client, therefore the 404 but on the server, it worked. Solved it with https://medium.com/the-tech-bench/next-js-environment-variables-d2f6ea1a1dca.

@andreaskeller hmmm ok i'll check it out. thank you this!!! i'm currently ONLY using my .env variables when the app is server render. I have a higher order component that uses dotenv to check to see it's CSR or SSR, if it is SSR I then dispatch the function needed to log the blog posts.

yeah, I've already got my variables working fine. I'm rendering that my /blog route using an HOC that gets my BLOGGER_API_KEY so it's working in those examples.... my /post/:ids child pages aren't using any env variables.

going to do some more digging, hopefully i can get to the bottom of it!

Its seems like most of the issues here are fixed. I'll work on this issue as well https://github.com/zeit/next.js/issues/1189

I'm closing this now. But If we need to do something in the core, let me know.

@sergiodxa as per our conversation last night, i've created the lil barebones app showing my issue. Let me know if you have any questions about it! I would be more than happy to fill you in on everything that is going on with it! 😄

It's not fixed.

i have the same problem but without any custom routes. I have /pages/pipeline.tsx and /pipeline url works just fine while /pipeline/ flashes 404 and then tries to load my page without calling "getInitialProps" called (since it was 404 on the server!).

@ex3ndr Not sure if this will fix the problem, but I had a similar issue and this cured it.
https://github.com/zeit/next.js#disabling-file-system-routing

Chances are next is trying to pull off the fileSystem before your custom route is hit.

@moaxaca I have solved the same way since i was needed to have my own custom routing - i just disabled next's one altogether.

I'm seeing this issue as well. @moaxaca's solution didn't work for me. What did was to use any Link component, but instead opt for using next-routes's pushRoute method.

Basic example:

import { connect } from 'react-redux'
import { Router } from '../routes'

// ----

export default class Link extends React.Component {

  handleClick(evt, url) {
    evt.preventDefault()    
    Router.pushRoute(url)
  }

  render() {
    const { url, title } = this.props

    return (
      <a href={ url} onClick={ (evt) => this.handleClick(evt, url) }>{title}</a>
    )
  }

}

This seems to figure it out and I don't see the Error Flashing. But it feels a bit quickfixy.

save issue

I got the same issue before and I was able to solve it. I'm using an express server set up. I found out that the reason why the 404 page flashes before loading to the right page is because my page slug doesn't match my component name.

When I have this code the 404 flashes:

server.get('/search', (req, res) => {
    const actualPage = '/search_results'
    const queryParams = { filters: req.query }
    app.render(req, res, actualPage, queryParams)
  })

Note that the /search and my actualPage values are different.

The 404 flashes disappears when I rename my component & the actualPage values to be the same as my page slug which is search. See working code below:

server.get('/search', (req, res) => {
    const actualPage = '/search'
    const queryParams = { filters: req.query }
    app.render(req, res, actualPage, queryParams)
  })

Now both are using /search.

Sorry for my english guys.

it may be a good idea to check you next.config.js useFileSystemPublicRoutes property. If it is false, that means you must define all the route handlers in server.js yourself.

This fixes the flashing 404 for me:

Link

<Link href="/somepage?id=value" as="/somepage/value">

Server side

server.get("/somepage/:id", (req, res) => {
  return app.render(req, res, "/maps", { id: req.params.id })
})

Access param on page

const { id } = this.props.url.query

So to answer this one once and for all:

href => path inside of the pages directory + the querystring
as => rendered in the browser url bar

Example:

You have a url called /products/:id

  1. You created pages/product.js
export default class extends React.Component {
  static async getInitialProps({query}) {
    console.log('SLUG', query.slug)
    return {}
  }
  render() {
    return <h1>The product page</h1>
  }
}
  1. You add the route to express or any other server, this is only for SSR. What this will do is route the url /products/:id to pages/product.js and provide id as part of query in getInitialProps:
server.get("/products/:slug", (req, res) => {
  return app.render(req, res, "/product", { slug: req.params.slug })
})
  1. For client side routing you use next/link like this:
<Link href="/product?slug=something" as="/products/something"> 

The reason you have to explicitly provide the href and as is that Next.js is not aware of any other pages on the client side. We don't send a manifest of all pages to the client side and subsequent routes get lazy-loaded, which is scalable.

There is a community package that abstracts away providing href and as by sending a pre-defined manifest of all routes, note that we recommend not sending a full manifest of all possible routes as not doing this is more scaleable

https://github.com/fridays/next-routes

Also, note that rendering 404 is no longer the default behavior of Next.js. Instead, we reload the page for the Zones feature.

Thanks Tim, totally clear explanation!

@timneutkens
If the Link's href and as. I pass the different value. How could I get href's value and as's value.
I test me demo. I think req.params.slug will be as's value. Then how could I get href's value.

@timneutkens You saved me I don't know how many hours of painful search for my issue :D

How are you rendering the Link component to that url? Are you using <Link href="/post/1"> or <Link href="/post?id=1" as="/post/1">?

You should put this in doc. :joy:

@sergiodxa

So to answer this one once and for all:

href => path inside of the pages directory + the querystring
as => rendered in the browser url bar

Example:

You have a url called /products/:id

  1. You created pages/product.js
export default class extends React.Component {
  static async getInitialProps({query}) {
    console.log('SLUG', query.slug)
    return {}
  }
  render() {
    return <h1>The product page</h1>
  }
}
  1. You add the route to express or any other server, this is _only_ for SSR. What this will do is route the url /products/:id to pages/product.js and provide id as part of query in getInitialProps:
server.get("/products/:slug", (req, res) => {
  return app.render(req, res, "/product", { slug: req.params.slug })
})
  1. For client side routing you use next/link like this:
<Link href="/product?slug=something" as="/products/something"> 

The reason you have to explicitly provide the href and as is that Next.js is not aware of any other pages on the client side. We don't send a manifest of all pages to the client side and subsequent routes get lazy-loaded, which is scalable.

There is a community package that abstracts away providing href and as by sending a pre-defined manifest of all routes, note that we recommend not sending a full manifest of all possible routes as not doing this is more scaleable

https://github.com/fridays/next-routes

Also, note that rendering 404 is no longer the default behavior of Next.js. Instead, we reload the page for the Zones feature.

what I can do if I only have an array of list header like:
[ home: '/home', about:'/about', post: 'post/:post_id?' ].
In next I only get as's value, but how about href ? Thanks :D

What if you're using router.push() instead of <Link> how can you achieve the same results?

@justinmchase I just had the same problem. Check Next Router documentation if you haven't already.

You can use it as Router.push(url, as, options):

Router.push('/post/[pid]', '/post/abc')

@timneutkens I would like to host my next.js app on ZEIT.now but that doesn't have an option to run an express server. Do I loose my pretty urls now and can only do param=value&param=value or is there another way? The example Sergio posted doesn't look promising

I tried dynamic routes but now my single page application is broken and links such as:

<Link href={`/integration?slug=${slug}`} as={`/integration/${slug}`}>

now cause a full page reload because http://localhost:8080/_next/static/development/pages/integration.js returns a 404 not found.

@timneutkens I would like to host my next.js app on ZEIT.now but that doesn't have an option to run an express server. Do I loose my pretty urls now and can only do param=value&param=value or is there another way? The example Sergio posted doesn't look promising

You don't need a custom server for dynamic routes, see https://nextjs.org/docs/routing/dynamic-routes

@timneutkens Thank you for your fast reply, I tried dynamic routes but will break single page application (see update). Running locally with next in my package.json script in dev mode.

Solved it by having a pages/integration/index.js with the code and a pages/integration/[slug].js with:

import ProductDetailPage from './index'
export default ProductDetailPage

The correct way to do what you outlined is to create pages/integration/[slug].js

And link to it in this way:

<Link href={`/integration/[slug]`} as={`/integration/${slug}`}>

Hi can I ask why when i call this one

Router.push( '/about?id=1234','/about')

the id cant be retrieve, and the page i am using is [...index].js which is a multi dynamic catch all page, but if I direct to other page that is not dynamic one it can work properly example car.js push to vehicle.js..
Is there a better way where i can push to my dynamic catch all page and then mask the id and still able to retrieve it..?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sospedra picture sospedra  ·  3Comments

renatorib picture renatorib  ·  3Comments

knipferrc picture knipferrc  ·  3Comments

jesselee34 picture jesselee34  ·  3Comments

DvirSh picture DvirSh  ·  3Comments