Dynamic routes and content
Dynamic content / Dynamic route / Static page
Next.js makes it easy to create dynamic routes that have params. These routes can still render static pages as well. Later on, we'll learn how to dynamically static generate these routes at run time 👀. So in our Known app, we have a blog. Right now this blog is broken at best. Lets use our CMS to get our blog working.
Static props
So I've been preaching that we can pull from any source to get data for our pages, did you know that includes the file system too? Yes, the filesystem. Next.js will eliminate all this server side code for the browser bundle which makes this possible. It's really magical. First, lets pull in all the blog post on the blog index page. We have two sources of blog posts though. Our CMS and our file system. I don't ever recommend doing that, this setups if for teaching purposes.
// fs in a react file? wooooow
import fs from 'fs'
import matter from 'gray-matter'
import path from 'path'
import orderby from 'lodash.orderby'
import { useRouter } from 'next/router'
import { posts as postsFromCMS } from '../../content'
const BlogPost = () => {
// component
const router = useRouter()
if (router.isFallback) {
return (
<Pane width="100%" height="100%">
<Spinner size={48} />
</Pane>
)
}
//
}
// at the bottom
export async function getStaticPaths() {
const postsDirectory = path.join(process.cwd(), 'posts')
const filenames = fs.readdirSync(postsDirectory)
const paths = filenames.map((name) => ({ params: { slug: name.replace('.mdx', '') } }))
// dont get paths for cms posts, instead, let fallback handle it
return { paths, fallback: true }
}
export async function getStaticProps(ctx) {
// read the posts dir from the fs
const postsDirectory = path.join(process.cwd(), 'posts')
const filenames = fs.readdirSync(postsDirectory)
// get each post from the fs
const filePosts = filenames.map((filename) => {
const filePath = path.join(postsDirectory, filename)
return fs.readFileSync(filePath, 'utf8')
})
// merge our posts from our CMS and fs then sort by pub date
const posts = orderby(
[...postsFromCMS.published, ...filePosts].map((content) => {
// extract frontmatter from markdown content
const { data } = matter(content)
return data
}),
['publishedOn'],
['desc'],
)
return { props: { posts } }
}
We must use getStaticPaths
with any page that has a parameter.
All we're doing here is reading all the post from the file system
and returning all the slugs. We're purposefully not getting the slugs
for the posts from the CMS. That could take a long time depending,
on how many we have there. So instead, we're going to use fallback: true
.
This prevents a 404 to a page that has not be statically rendered at build time,
and then fetches that page on demand only to statically generate and cache
it for future request. Your page will be notified when this happens so you
can show a loading screen if you'd like. Its basically on demand static generation.
If you're not a fan of the loading screen while your page builds, you can
use fallback: blocking
that does the same thing, except, the router won't
transition until the page is rendered. Very much like getServerSideProps
.
The flexibility here is amazing. As you'll see in a later lesson, we can even query a database to get our data without hitting an API.
Hit save and you should see a few posts at /blog
now. Next we'll fix the blog/[slug]
route.