1

Next.js beginner here, hoping for pointers on using getStaticPaths and getStaticProps with catch-all routes. Most Next.js 9.3+ blog starters seem to be based on just one level of blog posts (e.g., /posts/post-1.md, /posts/post-2.md, etc.), but what I've tried vainly to find is a starter — or just a set of instructions — that addresses handling, say, /posts/yyyy/mm/postname.md through /pages/posts/[...post].js.

I did read the Next.js docs regarding these items, of course, but I find them just a little short of helpful in at least this particular case. I do realize they're written for more experienced Next.js devs. This one item, from https://nextjs.org/docs/routing/dynamic-routes, gets me as close as I can get at the moment, but not quite far enough:

If the page name uses catch-all routes, for example pages/[...slug], then params should contain slug which is an array. For example, if this array is ['foo', 'bar'], then Next.js will statically generate the page at /foo/bar.

I tried using fs-readdir-recursive to read the /posts/ directory's various levels and that works, but what it gives me doesn't produce the array that getStaticPaths wants. I'm sure I just need to massage the result but can't find any examples to help me figure it out. (Most examples that do go further than the one-level scenario seem to deal with fetching from DBs, perhaps because the scenario I'm trying to find is considered too simple. Probably is, for non-beginners, but...)

Bryce Wray
  • 285
  • 3
  • 12

1 Answers1

6

If your posts all follow the same URL patterns, I would rather use the following structure:

pages/
└── posts/
    └── [year]/
        └── [month]/
            └── [slug].js

Depending on how you’re storing your posts, your getStaticPaths will only have to list the posts and expose year, month and slug for each post.

export async function getStaticPaths() {
  const posts = await getAllPosts()

  return {
    fallback: false,
    paths: posts.map(post => ({
      params: {
        year: post.year,
        month: post.month,
        slug: post.slug
      }
    })
  }
}

Then you’ll have access to all the year, month and slug parameters in getStaticProps.

export async function getStaticProps({params}) {
  // Retrieve blog post from year, month and slug
  const post = await getBlogPost({
    year: params.year,
    month: params.month,
    slug: params.slug
  })

  return {
    props: {
      post
    }
  }
}
Bertrand Marron
  • 21,501
  • 8
  • 58
  • 94
  • Thank you, Bertrand. That is indeed my usual pattern, but I couldn't tell from the docs how many items could be "loaded" in what goes in there, so to speak. Will give this a try. I had tried `[year]` and `[month]` folders but for some reason ran afoul. This makes sense. – Bryce Wray Aug 26 '20 at 17:51
  • Guess I then am wondering whether `getAllPosts` — which for most starters seems to use `fs.readdirSync("/path/to/posts")` — can be used just as readily with `fs-readdir-recursive` for going down multiple levels from the repo's root level to grab the posts' Markdown files. – Bryce Wray Aug 26 '20 at 18:11
  • 1
    `getAllPosts` can be anything you want, list files recursively (or not), retrieve data from a database, fetch from a remote API. It’ll all be static in the end. – Bertrand Marron Aug 26 '20 at 18:13
  • 1
    I am in the same exact situation, I combed through the docs but they fall a bit short. I still get errors when implementing the solution above. https://stackoverflow.com/questions/64163620/next-js-error-with-static-nested-route-content-directory-using-id-js – sn4ke Oct 02 '20 at 01:34