In this article, we will create a blog that supports two or more authors using Next. Js. We will attribute each post to a writer and display their name and photo along with the post. Each author will also have a profile page listing all the articles they have contributed. It’s going to look something like this.

We will keep all information in a file on our local file system. Two types of content, posts and authors, will use different types of files. Posts with large text volumes will use Markdown, which makes the editing process easier. Because the information about the author is light, we’ll put it in a JSON file. Helper functions will make it easier to read different file types and combine their contents.

Next. Js lets us effortlessly read from different sources and different types of data. Thanks to dynamic routing and next/ Link, we can quickly set up and navigate to the various pages of our website. We can also get image optimizations for free through the Next/Image package.

By selecting the “battery included” next.js, we can focus on our application itself. We didn’t have to spend any time on the repetitive groundwork that often comes with new projects. Instead of building everything by hand, we can rely on tested and proven frameworks. The large and active community behind next.js makes it easy to get help when we run into problems with it.

After reading this article, you will be able to add many kinds of content to a next.js project. You’ll also be able to build relationships between them. This allows you to connect things like the author to the post, the course to the course, or the actor to the movie.

This article assumes that you have basic familiarity with next.js. If you haven’t used it before, you might want to take a look at how it handles pages and fetches data for them.

In this article, we will not touch on modeling, but rather focus on making it all work. You can get the results on GitHub. If you want to follow this article, there is also a stylesheet that you can put into your project. To get the same framework, including navigation, replace your pages/_app.js with this file.

Set up the

First, we create a new project using the following method: create-next-app Creates a new project and switches to its directory.

$ npx create-next-app multiauthor-blog
$ cd multiauthor-blog
Copy the code

We will need to read the Markdown file later. To make it easier, we also added some dependencies before we started.

multiauthor-blog$ yarn add gray-matter remark remark-html
Copy the code

Once the installation is complete, we can run the dev script to start our project.

multiauthor-blog$ yarn dev
Copy the code

We can now explore our website. In your browser, open http://localhost:3000. You should see the default page added by create-next-app.

Later, we will need a navigation to get to our page. We can add pages to pages/_app.js before they even exist.

import Link from 'next/link' import '.. /styles/globals.css' export default function App({ Component, pageProps }) { return ( <> <header> <nav> <ul> <li> <Link href="/"> <a>Home</a> </Link> </li> <li> <Link href="/posts"> <a>Posts</a> </Link> </li> <li> <Link href="/authors"> <a>Authors</a> </Link> </li> </ul> </nav> </header> <main> <Component {... pageProps} /> </main> </> ) }Copy the code

In this article, we will add the missing pages that these navigations point to. Let’s start by adding some posts so we have something to use on the blog overview page.

Create a post

To keep our content separate from our code, we’ll put our posts in a directory called _posts/. To make writing and editing easier, we will create a Markdown file for each post. The file name of each post will be used as slug in our future routing. For example, the file _posts/hello-world.md will be accessible under /posts/hello-world.

Some information, such as full headings and short excerpts, will be placed in the preface at the beginning of the document.

--- title: "Hello World!" excerpt: "This is my first blog post." createdAt: "2021-05-03" --- Hey, how are you doing? Welcome to my blog. In this post,...Copy the code

Add a few of these files so your blog doesn’t start out empty.

Multi - an author - a blog / ├ ─ _posts / │ ├ ─ hello - world. Md │ ├ ─ multi - an author - a blog - in - nextjs. Md │ ├ ─ Styling - react - with - tailwind. Md │ └ ─ ten - hidden gems - in - javascript. Md └ ─ pages / └ ─...Copy the code

You can add your own files or grab sample articles from the GitHub repository.

List all posts

Now that we have some posts, we need a way to put them on our blog. Let’s start by adding a page that lists all the posts as an index to our blog.

In next.js, a file created under pages/posts/index.js will be on our site as /posts. The file must export a function as the body of the page. The first version of it looked something like this.

export default function Posts() {
  return (
    <div className="posts">
      <h1>Posts</h1>

      {/* TODO: render posts */}
    </div>
  )
}
Copy the code

We didn’t go very far because we didn’t have a way to read the Markdown file yet. We have been able to navigate to http://localhost:3000/posts, but we only see the title.

We now need a way to get our posts there. Next.js uses a function called getStaticProps() to pass data to a page component. This function passes props from the returned object to the component as props.

From getStaticProps(), we will pass the post to the component as a prop called posts. In this first step, we will hardcode two placeholder posts. Starting this way, we define the format of the actual posts we will receive later. If a helper function returns in this format, we can switch to it without changing the component.

The post overview does not show the full text of the post. For this page, the title, summary, fixed link, and date of each post are sufficient.

Export default function Posts() {... } +export function getStaticProps() { + return { + props: { + posts: [ + { + title: "My first post", + createdAt: "2021-05-01", + excerpt: "A short excerpt summarizing the post.", + permalink: "/posts/my-first-post", + slug: "my-first-post", + }, { + title: "My second post", + createdAt: "2021-05-04", + excerpt: "Another summary that is short.", + permalink: "/posts/my-second-post", + slug: "my-second-post", + } + ] + } + } +}Copy the code

To check for connections, we can grab Posts from the props and display them in the Posts component. We’ll include the title, creation date, excerpt, and a link to a post. Right now, the link doesn’t point anywhere.

+import Link from 'next/link' -export default function Posts() { +export default function Posts({ posts }) { return ( <div className="posts"> <h1>Posts</h1> - {/ TODO: render posts /} + {posts.map(post => { + const prettyDate = new Date(post.createdAt).toLocaleString('en-US', { + month: 'short', + day: '2-digit', + year: 'numeric', + }) + + return ( + <article key={post.slug}> + <h2> + <Link href={post.permalink}> + <a>{post.title}</a> + </Link> + </h2> + + <time dateTime={post.createdAt}>{prettyDate}</time> + + <p>{post.excerpt}</p> + + <Link href={post.permalink}> + <a>Read more →</a> + </Link> + </article> +) +})} export function getStaticProps() {... }Copy the code

After reloading the page in your browser, the two posts are now displayed.

We don’t want to hardcode all blog posts in getStaticProps() forever. After all, that’s why we created all these files in the _posts/ directory earlier. We now need a way to read these files and pass their contents to the page component.

There are several ways we can do this. We can read these files directly in getStaticProps(). Because this function runs on the server, not the client, we can access local Node.js modules such as fs from there. We can read, transform, and even manipulate local files in the same file as the page components.

To keep the document short and focused on one task, we will move these functions into a separate file. This way, the Posts component only needs to display the data, rather than read it itself. This adds some separation and organization to our project.

By convention, we’ll put the ability to read the data in a file called lib/api.js. This file will hold all the functions that fetch content for the component that displays it.

For the posts overview page, we need a function to read, process, and return all posts. We’ll call this getAllPosts(). In this function, we first use path.join() to establish the path to the _posts/ directory. We then read the directory using fs.readdirsync (), which gives us the names of all the files in it. These names are mapped, and then we read each file in turn.

import fs from 'fs'
import path from 'path'

export function getAllPosts() {
  const postsDirectory = path.join(process.cwd(), '_posts')
  const filenames = fs.readdirSync(postsDirectory)

  return filenames.map(filename => {
    const file = fs.readFileSync(path.join(process.cwd(), '_posts', filename), 'utf8')

    // TODO: transform and return file
  })
}
Copy the code

After reading the file, we get its contents as a long string. To separate the preface from the text of the article, we go through gray-matter. We will also get the luG for each post by removing the.md at the end of the file name. We need this lug to set up a URL so that we can access the post later. Since we don’t need the Markdown body of the post for this feature, we can ignore the rest of the content.

import fs from 'fs' import path from 'path' +import matter from 'gray-matter' export function getAllPosts() { const postsDirectory = path.join(process.cwd(), '_posts') const filenames = fs.readdirSync(postsDirectory) return filenames.map(filename => { const file = fs.readFileSync(path.join(process.cwd(), '_posts', filename), 'utf8') - // TODO: transform and return file + // get frontmatter + const { data } = matter(file) + + // get slug from filename + const slug = filename.replace(/.md$/, '') + + // return combined frontmatter and slug; build permalink + return { + ... data, + slug, + permalink: /posts/${slug}, + } }) }Copy the code

Notice how we put… Data to the returned object. This allows us to later get the value from its front contents, i.e. {post.title} instead of {post.data.title}.

Going back to our posts overview page, we can now replace placeholder posts with this new function.

+import { getAllPosts } from '.. /.. /lib/ API 'export default function Posts({Posts}) {... } export function getStaticProps() { return { props: { - posts: [ - { - title: "My first post", - createdAt: "2021-05-01", - excerpt: "A short excerpt summarizing the post.", - permalink: "/posts/my-first-post", - slug: "my-first-post", - }, { - title: "My second post", - createdAt: "2021-05-04", - excerpt: "Another summary that is short.", - permalink: "/posts/my-second-post", - slug: "my-second-post", - } - ] + posts: getAllPosts(), } } }Copy the code

After reloading the browser, we can now see the actual post instead of the placeholder.

Add a single post page

The links we add to each post don’t point anywhere yet. There is currently no page that responds to urls like /posts/hello-world. With dynamic routing, we can add a page that matches all paths, like this.

A file created as pages/posts/[slug].js will match all urls that look like /posts/ ABC. The value that appears in the URL instead of [slug] is supplied to the page as a query parameter. We can call a helper function on the corresponding page getStaticProps(), as params.slug.

As the corresponding function to getAllPosts(), we’ll call the helper function getPostBySlug(slug). It will return a post that matches the lug we passed to it, not all posts. On the page of a post, we also need to display the Markdown content of the underlying file.

Pages for individual posts look like pages for an overview of posts. In getStaticProps(), instead of passing posts, we just pass a post. Before we look at how to convert the Markdown content of a post into usable HTML, let’s do the general setup. We’ll skip the placeholder post here and use the helper function that we’ll add immediately in the next step.

import { getPostBySlug } from '.. /.. /lib/api' export default function Post({ post }) { const prettyDate = new Date(post.createdAt).toLocaleString('en-US', { month: 'short', day: '2-digit', year: 'numeric', }) return ( <div className="post"> <h1>{post.title}</h1> <time dateTime={post.createdAt}>{prettyDate}</time> {/ TODO: render body /} </div> ) } export function getStaticProps({ params }) { return { props: { post: getPostBySlug(params.slug), }, } }Copy the code

We will now add the function getPostBySlug(slug) to our auxiliary file lib/api.js. There are some obvious differences between this and getAllPosts(). Since we can get the file name of the post from slug, we don’t need to read the entire directory first. If slug is ‘hello-world’, we’ll read a file called _posts/hello-world.md. If the file does not exist, Next. Js will display a 404 error page.

Another difference from getAllPosts() is that this time we also need to read the Markdown content of the post. We can start with remark and return it as renderable HTML instead of raw Markdown.

import fs from 'fs' import path from 'path' import matter from 'gray-matter' +import remark from 'remark' +import html From 'remark-html' export function getAllPosts() {... } +export function getPostBySlug(slug) { + const file = fs.readFileSync(path.join(process.cwd(), '_posts', ${slug}.md), 'utf8') + + const { + content, + data, + } = matter(file) + + const body = remark().use(html).processSync(content).toString() + + return { + ... data, + body, + } +}Copy the code

In theory, we could use the function getAllPosts() in getPostBySlug(slug). We first get all the posts with it, and then we can search for posts that match a given lug. This will mean that we always need to read all the posts before we get one, which is unnecessary work. GetAllPosts () also does not return the Markdown content of the post. We can update it to do this, in which case it will do more work than is currently needed.

Because these two helper functions do different things, we need to separate them. In this way, we can focus these functions on the only job we need each of them to do.

Pages that use dynamic routing can provide a getStaticPaths() next to their getStaticProps(). This function tells next.js which dynamic path segment values to build pages for. We can provide this by using getAllPosts() and returning a list of objects that define each post’s slug.

-import { getPostBySlug } from '.. /.. /lib/api' +import { getAllPosts, getPostBySlug } from '.. /.. /lib/ API 'export default function Post({Post}) {... } export function getStaticProps({params}) {... } +export function getStaticPaths() { + return { + fallback: false, + paths: getAllPosts().map(post => ({ + params: { + slug: post.slug, + }, + })), + } +}Copy the code

Since we parsed Markdown’s content in getPostBySlug(Slug), we can now render it on the page. We need to use dangerouslySetInnerHTML at this step so that Next. Js can render the HTML after post.body. Despite its name, it is safe to use this property in this case. Because we have complete control over our posts, they are less likely to inject unsafe scripts.

import { getAllPosts, getPostBySlug } from '.. /.. /lib/api' export default function Post({ post }) { const prettyDate = new Date(post.createdAt).toLocaleString('en-US', { month: 'short', day: '2-digit', year: 'numeric', }) return ( <div className="post"> <h1>{post.title}</h1> <time dateTime={post.createdAt}>{prettyDate}</time> - {/ TODO: render body /} + <div dangerouslySetInnerHTML={{ __html: Post.body} /> </div>)} export function getStaticProps({params}) {... } export function getStaticPaths() {... }Copy the code

If we find a link from the post overview, we can now go to the post’s own page.

Add the author

Now that we have posts, we need to repeat the same steps for our authors. This time, we’ll use JSON instead of Markdown to describe them. We can mix different types of files in the same project whenever it makes sense. The helper functions we use to read the file handle any discrepancies for us. Pages can use these functions without knowing what format we store content in.

First, create a directory called _authors/ and add some author files to it. Just like we did with posts, name the files after each author’s entry. We’ll use it later to find the author. In each file, we specify an author’s full name in a JSON object.

{
  "name": "Adrian Webber"
}
Copy the code

For now, two authors are sufficient for our project.

To personalize them, we also added a profile picture for each author. We’ll put these static files in the public/ directory. By naming these files after the same slugs, we can connect them with just the implied convention. We can link the two by adding the path of the image to each author’s JSON file. By naming all files with slugs, we can manage this connection without having to write it out. JSON objects only need to hold information that we can’t build in code.

When you’re done, your project catalog should look something like this.

Multi - an author - a blog / ├ ─ _authors / │ ├ ─ Adrian - webber. Json │ └ ─ megan - carter. Json ├ ─ _posts / │ └ ─... ├─ Exercises - ├─ ├─ ├─ manu-manu-manu-manu-manu-manu-manu-manu.txtCopy the code

As with posts, we now need helper functions to read all authors and get individual authors. The new functions getAllAuthors() and getAuthorBySlug(slug) are also in lib/api.js. They do almost exactly the same thing as the corresponding functions for posts. Because we use JSON to describe the author, we don’t need to parse any Markdown with remark. We also do not need gray-matter to resolve FrontMatter. Instead, we can use JavaScript’s built-in json.parse () to read the text content of our files as objects.

Const contents = fs.readfilesync (somePath, 'utf8') const contents = fs.readfilesync (somePath, 'utf8') "John Doe"}' const json = json. Parse (contents) // a real JavaScript object we can do things with "John Doe" }Copy the code

With that knowledge, our helper function looks something like this.

Export function getAllPosts() {... } export function getPostBySlug(slug) {... } +export function getAllAuthors() { + const authorsDirectory = path.join(process.cwd(), '_authors') + const filenames = fs.readdirSync(authorsDirectory) + + return filenames.map(filename => { + const file = fs.readFileSync(path.join(process.cwd(), '_authors', filename), 'utf8') + + // get data + const data = JSON.parse(file) + + // get slug from filename + const slug = filename.replace(/.json/, '') + + // return combined frontmatter and slug; build permalink + return { + ... data, + slug, + permalink: /authors/${slug}, + profilePictureUrl: ${slug}.jpg, + } + }) +} + +export function getAuthorBySlug(slug) { + const file = fs.readFileSync(path.join(process.cwd(), '_authors', ${slug}.json), 'utf8') + + const data = JSON.parse(file) + + return { + ... data, + permalink: /authors/${slug}, + profilePictureUrl: /${slug}.jpg, + slug, + } +}Copy the code

With the method of reading authors in our application, we can now add a page that lists all authors. Create a new page under pages/authors/index.js and you have the /authors page on your site.

The helper functions take care of reading files for us. The page component does not need to know that the author is a JSON file in the file system. It can use getAllAuthors() without knowing where or how it got the data. Format doesn’t matter as long as our helper function returns data in a format we can use. Abstractions like this let us mix different types of content in our applications.

The author’s index page looks a lot like the post’s index page. We get all the Authors in getStaticProps() and pass them to the Authors component. This component maps each author and lists some information about them. We don’t need to build any additional links or urls from the slug. The help function already returns the author in a usable format.

import Image from 'next/image' import Link from 'next/link' import { getAllAuthors } from '.. /.. /lib/api/authors' export default function Authors({ authors }) { return ( <div className="authors"> <h1>Authors</h1> {authors.map(author => ( <div key={author.slug}> <h2> <Link href={author.permalink}> <a>{author.name}</a> </Link> </h2> <Image alt={author.name} src={author.profilePictureUrl} height="40" width="40" /> <Link href={author.permalink}> <a>Go To profile →</a> </Link> </div>))} </div>)} export function getStaticProps() {return {props: {authors: getAllAuthors(), }, } }Copy the code

If we visit /authors on our website, we see a list of all authors, including their names and images.

The link to the author profile doesn’t point anywhere yet. To add an introduction page, we create a file under pages/authors/[slug].js. Since the author doesn’t have any text, all we can add now is their name and profile picture. We also need another getStaticPaths() that tells next.js which slugs to build pages for.

import Image from 'next/image' import { getAllAuthors, getAuthorBySlug } from '.. /.. /lib/api' export default function Author({ author }) { return ( <div className="author"> <h1>{author.name}</h1> <Image alt={author.name} src={author.profilePictureUrl} height="80" width="80" /> </div> ) } export function getStaticProps({ params }) { return { props: { author: getAuthorBySlug(params.slug), }, } } export function getStaticPaths() { return { fallback: false, paths: getAllAuthors().map(author => ({ params: { slug: author.slug, }, })), } }Copy the code

With this, we now have a basic author profile page with very little information.

At this point, the author and the post have not been connected. Next we will build this bridge so that we can add a list of each author’s posts to their profile page.

Connect posts to authors

To connect two pieces of content, we need to reference one in the other. Since we already identify posts and authors by slug, we’ll use it to reference them. We can add authors to posts and posts to authors, but one direction is enough to connect them. Since we want to attribute the post to the author, we add the author’s slug to the preface of each post.

--- title: "Hello World!" excerpt: "This is my first blog post." createdAt: "2021-05-03" +author: adrian-webber --- Hey, how are you doing? Welcome to my blog. In this post,...Copy the code

If we keep it that way, add the author field as a string to the post via gray-matter.

const post = getPostBySlug("hello-world")
const author = post.author

console.log(author)
// "adrian-webber"
Copy the code

To get an object representing the author, we can use the lug and call getAuthorBySlug(slug).

 const post = getPostBySlug("hello-world")
-const author = post.author
+const author = getAuthorBySlug(post.author)

 console.log(author)
 // {
 //   name: "Adrian Webber",
 //   slug: "adrian-webber",
 //   profilePictureUrl: "/adrian-webber.jpg",
 //   permalink: "/authors/adrian-webber"
 // }
Copy the code

To add an author to the page of an individual post, we need to call getAuthorBySlug(slug) once in getStaticProps().

+import Image from 'next/image' +import Link from 'next/link' -import { getPostBySlug } from '.. /.. /lib/api' +import { getAuthorBySlug, getPostBySlug } from '.. /.. /lib/api' export default function Post({ post }) { const prettyDate = new Date(post.createdAt).toLocaleString('en-US', { month: 'short', day: '2-digit', year: 'numeric', }) return ( <div className="post"> <h1>{post.title}</h1> <time dateTime={post.createdAt}>{prettyDate}</time> + <div> + <Image alt={post.author.name} src={post.author.profilePictureUrl} height="40" width="40" /> + + <Link href={post.author.permalink}> + <a> + {post.author.name} + </a> + </Link> + </div> <div dangerouslySetInnerHTML={{ __html: post.body }}> </div> ) } export function getStaticProps({ params }) { + const post = getPostBySlug(params.slug) return {  props: { - post: getPostBySlug(params.slug), + post: { + ... post, + author: getAuthorBySlug(post.author), + }, }, } }Copy the code

Please note how we put… The post is propagated into an object also called POST in getStaticProps(). By placing author after this line, we end up replacing the string version of the author with its full object. This allows us to access an author property via post.author.name in the Post component.

With this change, we now get a link on the post’s page to the author’s profile page, including their name and photo.

Adding authors to the article overview page requires a similar change. We need to map all posts and call getAuthorBySlug(slug) for each post instead of just once.

+import Image from 'next/image' +import Link from 'next/link' -import { getAllPosts } from '.. /.. /lib/api' +import { getAllPosts, getAuthorBySlug } from '.. /.. /lib/api' export default function Posts({ posts }) { return ( <div className="posts"> <h1>Posts</h1> {posts.map(post => { const prettyDate = new Date(post.createdAt).toLocaleString('en-US', { month: 'short', day: '2-digit', year: 'numeric', }) return ( <article key={post.slug}> <h2> <Link href={post.permalink}> <a>{post.title}</a> </Link> </h2> <time dateTime={post.createdAt}>{prettyDate}</time> + <div> + <Image alt={post.author.name} src={post.author.profilePictureUrl} height="40" width="40" /> + + <span>{post.author.name}</span> + </div> < p > {post. Excerpt} < / p > < Link href = {post. The permalink} > < a > Read more - > < / a > < / Link > < article >)})} < / div >)} export function getStaticProps() { return { props: { - posts: getAllPosts(), + posts: getAllPosts().map(post => ({ + ... post, + author: getAuthorBySlug(post.author), + })), } } }Copy the code

This allows you to add an author to each post in the Post overview.

We don’t need to add the author’s list of posts to his JSON file. On their profile page, we first get all the posts with getAllPosts(). We can then filter out articles attributed to that author from the complete list.

import Image from 'next/image' +import Link from 'next/link' -import { getAllAuthors, getAuthorBySlug } from '.. /.. /lib/api' +import { getAllAuthors, getAllPosts, getAuthorBySlug } from '.. /.. /lib/api' export default function Author({ author }) { return ( <div className="author"> <h1>{author.name}</h1> <Image alt={author.name} src={author.profilePictureUrl} height="40" width="40" /> + <h2>Posts</h2> + + <ul> + {author.posts.map(post => ( + <li> + <Link href={post.permalink}> + <a> + {post.title} + </a> + </Link> + </li> + ))} + </ul> </div> ) } export function getStaticProps({ params }) { const author = getAuthorBySlug(params.slug) return { props: { - author: getAuthorBySlug(params.slug), + author: { + ... author, + posts: GetAllPosts ().filter(post => post.author === = author.slug), +},},}} export function getStaticPaths() {... }Copy the code

This gives us a list of articles on each author’s profile page.

On the author overview page, we just add how many articles they have written to avoid clutter.

import Image from 'next/image' import Link from 'next/link' -import { getAllAuthors } from '.. /.. /lib/api' +import { getAllAuthors, getAllPosts } from '.. /.. /lib/api' export default function Authors({ authors }) { return ( <div className="authors"> <h1>Authors</h1> {authors.map(author => ( <div key={author.slug}> <h2> <Link href={author.permalink}> <a> {author.name} </a> </Link> </h2> <Image alt={author.name} src={author.profilePictureUrl} height="40" width="40" /> + <p>{author.posts.length} Post (s)</p> <Link href={author.permalink}> <a>Go to profile →</a> </Link> </div>))} </div>)} export function getStaticProps() { return { props: { - authors: getAllAuthors(), + authors: getAllAuthors().map(author => ({ + ... author, + posts: getAllPosts().filter(post => post.author === author.slug), + })), } } }Copy the code

With this, the author overview page shows how many articles each author has contributed.

That’s it! Now, the post and the author are completely connected. We can go from one post to an author’s profile page and from there to their other posts.

Summary and Outlook

In this article, we connect through unique slugs of two related types of content. It defines the relationship from the post to the author and realizes the application of various scenarios. We can now show authors on each post and list their posts on their profile page.

With this technique, we can add many other types of relationships. Each post may have one commenter above the author. We can set this up by adding a Reviewer field in the preface to a post.

--- title: "Hello World!" excerpt: "This is my first blog post." createdAt: "2021-05-03" author: adrian-webber +reviewer: megan-carter --- Hey, how are you doing? Welcome to my blog. In this post,...Copy the code

In the file system, the reviewer is another author in the _authors/ directory. We can also use getAuthorBySlug(Slug) to get their information.

export function getStaticProps({ params }) { const post = getPostBySlug(params.slug) return { props: { post: { ... post, author: getAuthorBySlug(post.author), + reviewer: getAuthorBySlug(post.reviewer), }, }, } }Copy the code

We can even support co-authors, naming two or more authors on a post instead of just one person.

--- title: "Hello World!" excerpt: "This is my first blog post." createdAt: "2021-05-03" -author: adrian-webber +authors: + - adrian-webber + - megan-carter --- Hey, how are you doing? Welcome to my blog. In this post,...Copy the code

In this case, we can no longer look for a single author in a post’s getStaticProps(). Instead, we’ll map the authors array to get all of them.

export function getStaticProps({ params }) { const post = getPostBySlug(params.slug) return { props: { post: { ... post, - author: getAuthorBySlug(post.author), + authors: post.authors.map(getAuthorBySlug), }, }, } }Copy the code

We can also use this technique to generate other types of scenarios. It can implement any type of one-to-one, one-to-many, or even many-to-many relationship. If your project also has press releases and case studies, you can add them to each author as well.

On a website about the Marvel Universe, we can associate characters with the movies in which they appear. In sports, we can relate players to the teams they currently play for.

Because helper functions hide data sources, content can come from different systems. We can read articles from the file system, read comments from the API, and incorporate them into our code. If something is related to another type of content, we can use this pattern to connect them.

More resources

Next. Js provides more background on the functions we use in its page on data fetching. It includes links to sample projects that get data from different types of sources.

If you want to learn more about this startup, check out these articles.

  • Build a clone of the CSS Tricks website with Strapi and next.js replacing files on the local file system with a strapi-driven back end.
  • Compare styling methods in next.js to explore different ways to write custom CSS to change the styling of the launcher.
  • Markdown/MDX with next.js Add MDX to your project so you can use JJSX and React components in Markdown.