How to setup dynamic content in a Next.js site

In the previous post, Linking two pages in Next.js using Link, we learned how to link the home page to the blog page.

Next.js is a great framework for building blogs, and in this chapter, we will explore how to add dynamic content to our blog by creating blog posts.

Blog posts have dynamic URLs, such as /blog/hello-world or /blog/my-second-post. This dynamic content can be sourced from a database, markdown files, or any other sources.

Next.js allows us to serve dynamic content based on dynamic URLs. To create a dynamic URL, we need to create a dynamic page using the [] syntax.

Here’s how it works:

  1. Create a file called pages/blog/[id].js.
  2. This file will handle all the dynamic URLs for the /blog/ route.
  3. The [id] inside the square brackets in the file name signifies that any dynamic values will be put inside the id parameter of the query property in the router.

Now, let’s apply these concepts in practice.

Create the file pages/blog/[id].js with the following code:

import { useRouter } from "next/router";

export default () => {
 const router = useRouter();

 return (
 <>
 <h1>Blog post</h1>
 <p>Post id: {router.query.id}</p>
 </>
 );
};

When you visit http://localhost:3000/blog/test in your browser, you should see the following output:

Screen_Shot_2019-11-05_at_16.41.32.png

We can use the id parameter to fetch the corresponding post from a list of posts. For simplicity, let’s create a posts.json file in the project root folder:

{
 "test": {
 "title": "test post",
 "content": "Hey some post content"
 },
 "second": {
 "title": "second post",
 "content": "Hey this is the second post content"
 }
}

In your pages/blog/[id].js file, add the following code:

import { useRouter } from "next/router";
import posts from "../../posts.json";

export default () => {
 const router = useRouter();

 const post = posts[router.query.id];

 return (
 <>
 <h1>{post.title}</h1>
 <p>{post.content}</p>
 </>
 );
};

Now, when you reload the page, you should see the correct blog post content.

However, you may encounter an error in the console and the browser:

Screen_Shot_2019-11-05_at_18.18.17.png

This error occurs because the data is not available during the rendering process. To fix this issue, add a conditional check before rendering the JSX:

import { useRouter } from "next/router";
import posts from "../../posts.json";

export default () => {
 const router = useRouter();

 const post = posts[router.query.id];
 if (!post) return <p></p>;

 return (
 <>
 <h1>{post.title}</h1>
 <p>{post.content}</p>
 </>
 );
};

Now, everything should work as expected. Initially, the component is rendered without the dynamic router.query.id information. After rendering, Next.js triggers an update with the query value, and the page displays the correct information. If you view the page source, you will notice an empty <p> tag in the HTML.

We will soon address this issue to implement server-side rendering (SSR), which improves loading times for users and enhances SEO and social sharing. In the next lesson, we will learn how to provide data to the component with the getInitialProps method.

To complete the blog example, let’s list all the posts in the pages/blog.js file:

import posts from "../posts.json";

const Blog = () => (
 <div>
 <h1>Blog</h1>

 <ul>
 {Object.entries(posts).map((value, index) => {
 return <li key={index}>{value[1].title}</li>;
 })}
 </ul>
 </div>
);

export default Blog;

We can also link each post to its individual page. To do this, import the Link component from next/link and use it within the posts loop:

import Link from "next/link";
import posts from "../posts.json";

const Blog = () => (
 <div>
 <h1>Blog</h1>

 <ul>
 {Object.entries(posts).map((value, index) => {
 return (
 <li key={index}>
 <Link href="/blog/[id]" as={"/blog/" + value[0]}>
 <a>{value[1].title}</a>
 </Link>
 </li>
 );
 })}
 </ul>
 </div>
);

export default Blog;

Tags: Next.js, dynamic content, router, SEO, blog posts