6
votes

I'm trying to create a blog page to test nextjs and created a dynamic route for the posts, which will be retrieved from Contentful. When navigating from Home page and clicking into a next/router <Link /> component, the blog post loads correctly, but if I get the URL and try loading the page directly from browser address bar, I'll get 404.

Steps to reproduce:

 1. git clone https://github.com/zeit/next-learn-demo.git
 2. cd next-learn-demo/8-deploying
 3. yarn
 4. next build && next export
 5. cd out
 6. serve
 7. Navigate to http://localhost:5000/p/learn-nextjs
 8. See 404

Is this a limitation of NextJS (didn't find anything related to it on documentation) or do we need to configure anything else?

4

4 Answers

6
votes

The real issue is that exporting a next app will make it generate static HTML files. Even though it will still be able to request data before rendering the page, the set of available paths are not dynamic (they are generated during the next export command). See this docs and this example.

Based on this, I have 2 possible solutions:

  • generate a webhook to trigger a next build && next export command every time a new blog post is published in Contentful;
  • avoid exporting my next app and host a Node server that will handle the dynamic routes.
2
votes

That's because when you directly access the link or refresh the page then it add's a slash at the end of route. An next.js doesn't recognize any route like that. To fix this, I hope there should be an easiest way to do that. However you can do this using custom server. Here is an example:

server.get("/about/", (req, res) => {
  return app.render(req, res, "/about")
});

Hope this will help you.

1
votes

To extend the answer provided by @Minoru, the official Next documentation covers this case in this example.

Using getStaticPaths and getStaticProps allows you to create dynamic pages at build time, avoiding the 404.

Example code for posts dynamic page:

import { GetStaticPaths, GetStaticProps } from 'next';

const Post = ({ post }) => {
    const { id, content } = post;

    return (
        <div>
            <h1>Post {id}</h1>
            <p>{content}</p>
        </div>
    );
};

export const getStaticPaths: GetStaticPaths = async () => {
    // Get all posts via API, file, etc.
    const posts = [{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }, { id: '5' }]; // Example
    const paths = posts.map(post => ({
        params: { id: post.id },
    }));
    return { paths, fallback: false };
};

export const getStaticProps: GetStaticProps = async context => {
    const postId = context.params?.id || '';
    // Get post detail via API, file, etc.
    const post = { id: postId, content: `I'm the post with id ${postId}!` }; // Example
    return { props: { post } };
};

export default Post;

When building the site with next build && next export we will see in the out folder that Next has created each post page

posts in out folder

Then, when you navigate to /posts/3/ you will see the post with id 3

post with id 3 page

For reference, this docs page contains this case and many other use cases.

0
votes

Don't want to infringe any old posts rules, but in case anyone else in my context I use vercel's feature webhook to new deploys and as I was using firebase I've created a simple firebase function whith is hooked to a new event creation of a page triggers the webhook. I've used fetch because we can make a GET request according to the docs

exports.newEventAdded = functions.region('us-central1').firestore.document('local_events/{localeventId}')
.onCreate((snap, context) => {
    fetch('https://api.vercel.com/v1/integrations/deploy/process.env.NEXT_PUBLIC_VERCEL_WEBHOOK_ID')
        .then(function (response) {
            return response.json();
        })
        .then(function (myJson) {
            console.log(JSON.stringify(myJson));
        });
})