Building a blog with Next.js
- react
- nextjs
- typescript
One of the most important frameworks for React applications is now Next.js. It enables developers to create better React applications without complexity for server-side rendering.
Today, the greatest alternative for people who want a simple but effective blog—without having to write a lot of code and while improving our SEO is to create one using Next.js.
In this tutorial, we'll use Next.js to create a static-generated, production-ready blog. We'll go through the process of static site generation (SSG) and create a fantastic blog with strong SEO.
Getting started
We'll start by creating a new Next.js application, To get started, we'll use the following command:
npx create-next-app@latest site --typescript
we'll structure our application, Our application structure will be like the following:
- site
| - lib
| - mdx.ts
| - types.ts
| - pages
| - blog
| - [slug].tsx
| - _app.tsx
| - _document.tsx
| - blog.tsx
| - posts
| - 🎉.mdx
Now, we'll install all the dependencies that we'll need.
npm i --save-dev @types/node gray-matter next-mdx-remote rehype rehype-autolink-headings rehype-code-titles rehype-prism-plus rehype-slug remark-gfm
we'll write the code for the mdx.ts file.
import { serialize } from 'next-mdx-remote/serialize';
import remarkGfm from 'remark-gfm';
import rehypeSlug from 'rehype-slug';
import rehypeCodeTitles from 'rehype-code-titles';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypePrism from 'rehype-prism-plus';
export async function mdxToHtml(source: string) {
const mdxSource = await serialize(source, {
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [
rehypeSlug,
rehypeCodeTitles,
rehypePrism,
[
rehypeAutolinkHeadings,
{
properties: {
className: ['anchor']
}
}
]
],
format: 'mdx'
}
});
return {
html: mdxSource
};
}
A brief description of what's happening inside this file and what each function is doing is given below:
serializeis allowing MDX to be loaded withingetStaticPropsorgetServerSidePropsand hydrated correctly on the client.remarkGfmThis package is a unified (remark) plugin to enable the extensions to markdown that GitHub adds: autolink literals (
www.x.com), footnotes ([^1]), strikethrough (~~stuff~~), tables (| cell |…), and tasklists (\* [x]). You can use this plugin to add support for parsing and serializing them. These extensions by GitHub to CommonMark are called GFM (GitHub Flavored Markdown).rehypePluginsThis option puts all the extras you want like:rehypeSluggives headersids. It searches for headings (h1throughh6) withoutids and addsidattributes to them based on the content included in those headings.rehypeCodeTitlesplugin to add code title.rehypePrismplugin to highlight code blocks in HTML with Prism (via refractor) with additional line highlighting and line numbers functionalities.rehypeAutolinkHeadingsplugin to add links to headings withids back to themselves and you can change the name of theclassNameof the links
we'll create a types.ts file inside of our lib folder, where we'll construct all of our TypeScript interfaces and types.
Put the following code inside the file:
import { MDXRemoteSerializeResult } from 'next-mdx-remote';
export interface PostPageType {
slug: string;
frontmatter: {
title: string;
description: string;
date: string;
};
content: MDXRemoteSerializeResult;
}
export interface BlogPostType {
[key: string]: Array<PostPageType>;
}
Rendering an article as a preview
First, we'll write data into the posts/🎉.mdx file:
---
title: party
description: We'll celebrate now.
date: 2022-08-31
---
The getStaticProps function in Next.js will be used to retrieve all of our blog posts and inside the function, matter will be used to parse front-matter from a string or
file into the blog.tsx page. The following code will now be pasted into our blog.tsx file:
import { readdirSync, readFileSync } from 'fs';
import { join } from 'path';
import matter from 'gray-matter';
import Link from 'next/link';
import { BlogPostType } from 'lib/types';
export default function Blog({ posts }: BlogPostType) {
return (
<article className="wrapper">
<h1>Blogs</h1>
<div className="margin-top-700">
<ol className="auto-grid" role="list">
{posts.map((post, index) => {
return (
<li className="card" key={index + 1}>
<article className="flow">
<h3 className="fs-600">{post.frontmatter.title}</h3>
<p className="fs-300">{post.frontmatter.description}</p>
<Link href={`/blog/${post.slug}`}>
<a className="button">Read More</a>
</Link>
</article>
</li>
);
})}
</ol>
</div>
</article>
);
}
export async function getStaticProps() {
const files = readdirSync(join(process.cwd(), 'posts'));
const posts = files.map((filename) => {
// Create slug
const slug = filename.replace('.mdx', '');
const markdownWithMeta = readFileSync(
join(process.cwd(), 'posts', filename),
'utf-8'
);
const { data: frontmatter } = matter(markdownWithMeta);
return { slug, frontmatter };
});
return {
props: {
posts: posts
}
};
}
Rendering our blog posts
First, we'll import whoever matter, mdxToHtml and MDXRemote in [slug].tsx page, and use getStaticPaths and getStaticProps.
- In the
getStaticPathsfunction we'll fetch the post file paths - We'll retrieve
slugas a property in thegetStaticPropsfunction to get post file paths for use inmatterto bringfrontmatterandcontent
The following code will now be pasted:
import { readdirSync, readFileSync } from 'fs';
import { join } from 'path';
import matter from 'gray-matter';
import { mdxToHtml } from 'lib/mdx';
import { MDXRemote } from 'next-mdx-remote';
import { PostPageType } from 'lib/types';
export default function PostPage({
frontmatter: { title },
content
}: PostPageType) {
return (
<article className="wrapper">
<div className="[ post ] [ flow ]">
<h1>{title}</h1>
<hr />
<MDXRemote {...content} />
</div>
</article>
);
}
export async function getStaticPaths() {
const files = readdirSync(join(process.cwd(), 'posts'));
const paths = files.map((filename) => ({
params: { slug: filename.replace('.mdx', '') }
}));
return { paths, fallback: false };
}
export async function getStaticProps({ params: { slug } }) {
const markdownWithMeta = readFileSync(
join(process.cwd(), 'posts', slug + '.mdx'),
'utf-8'
);
const { data: frontmatter, content } = matter(markdownWithMeta);
const { html } = await mdxToHtml(content);
return {
props: { frontmatter, slug, content: html }
};
}
Conclusion
Using Next.js to create a blog is really simple and uncomplicated. There are several advantages to using Next.js, particularly for blogs. Your blog application will run very well, be lightweight, and have a high SEO ranking.