What are Next.js & MDX
Next.js
Next.js is a open-sourced and React-based framework. We can create SSR and SGR app easiily, and it also support ISR with SWR library. There are completed documents and many examples for us to learn Next.js, one of cases is using MDX/MD on Next.js.
MDX
MDX allow we use JSX in the markdown content. There are plugins providing a good integration on it, we can use custom components or override the markdown sytnx with our components. It's a straightforward way for us writing blog and with less burdensome on maintenance.
How to start?
Next.js provides a CLI tool enabled us to quickliy start building a new Next.js application, we don't need to set up everything by ourselves. I'll use npm in my case showing.
npx create-next-app@latest
If we would like to use TypeScript, the CLI tool also provides --ts
or --typescript
flag.
npx create-next-app@latest --ts
After entered this command line, we need to enter y
and our project name.

Then, it will install dependency packages. After installation completed, there are a successful information and some scripts tips to start running project.

Enter the command line, npm run dev
, and open link http://localhost:3000
to see the Next.js app running on the browser.

A .next
directory was generated with npm run dev
, there are cache, server, and static files of our app.
Pages and routing
In Next.js, all the site pages rendered by the files under pages
directory, there are multiple page name rules to map the router.
We need pages for our app, one of is to list all posts and the other one is the content of every post. We create a folder named blog
, and then create blog/index.tsx
to list posts and blog/[slug].tsx
for post content.
The directory as shown below, followed Next.js router rule, the default file under blog
is blog/index.tsx
. When we run on local, the address in blog page is http:localhost:3000/blog
.
The blog/[slug].tsx
is a way to map dynamic path name, the address will be http:localhost:3000/blog/[dynamic-path-name]
, [dynamic-path-name]
setted by Next.js method called getStaticPaths
.

And create _posts
folder to place all post wrote with MDX, and there are two mdx file as posts sample.

How to write MDX
Here's the content what MDX looked like, we can use markdown with html tag, and jsx.
---
title: "Build a blog by yourself 01 - introduction"
date: 'January 18, 2022'
description: 'I would like to be a the enthusiasm ...'
thumbnailUrl: '/assets/blog/how-to-build-this-site/how-to-build-this-site.jpeg'
tags: ['blog', 'javascript', 'Next.js']
---
## The motivation
I would like to be a the enthusiasm and professional engineer.
Keep learning and willing to share to improve myself,
so I start to write some blogs about what I learned or something interesting.
...
#### Blog and Posts
<img src="/assets/blog/how-to-build-this-site/wireframe-blog.jpg" alt="Wireframe home" width="100%"/>
<br/>
...
### Use these to improve development
* [eslint](https://eslint.org/)
* [prettier](https://prettier.io/)
* [typescript](https://www.typescriptlang.org/)
* [husky](https://typicode.github.io/husky/#/)
<PostThumbnail
slug="demo"
thumbnailUrl="demo.png"
title="demo title"
date="'January 18, 2022'"
description="demo description"
/>
...
On the top of content enclosed with ---
, it's a post metadata used YAML sytnx. The sytnx use key and value mapping to write any information on it.
We also could set our custom component, the <PostThumbnail>
is the example as shown below to link other post:
Build a blog by yourself 01 - introduction
I would like to be a the enthusiasm and professional engineer. Keep learning and willing to share to
Building blog page
There're two matters we need to do, one is list all posts in blog/index.tsx
, the other one is showing MDX content in blog/[slug].tsx
.
Next.js page and fetch data
In Next.js page
In Next.js, we can use a getStaticProp
method to fetch data which running at build time, and return some data, like props data used in page.
As follows shown, the basic code looked like this:
const Blog: NextPage<Props>= (props: Props) => {{
return (
<div></div>
)
}
export const getStaticProps = async () => {
return {
props: {
// props data
}
}
}
export default Blog
But if we need post list in pagination by url query parameter, such as http://localhost:3000/blog?page=1
. We could not get the url query in getStaticProp
, because it's only invoked on a build time, and not refresh data when queries are changed.
Next.js provide other method is GetServerSideProps
, it's primarily used to fetch data for every time when user with different request, like query paramters are changed.
This method is used like getStaticProp
, but we can know the query from user request.
export const getServerSideProps: GetServerSideProps = async ({ query }) => {
// We can use query for data fetching
return {
props: {
// props data
},
};
};
Fetch data from _posts folder
To fetch data, we need to read all MDX files from _post
directory. I prefer to use an API separately to fetch data, next create lib
folder then add a new file named lib/api.tsx
.
Before fetching data, we need install the gray-matter
to get metadata from MDX file.
npm install -D gray-matter
In api.tsx
, the first thing is to import fs
, path
, and gray-matter
.
// api.tsx
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
write a method to get metadata from all posts afterwards.
Use method, fs.readdirSync
, to get files name array in _posts
folder, map file name as path and get metatdata by gray-matter
.
// api.tsx
...
const postsDirectory = path.join('_posts');
const readFile = (fileName: string) => {
const postPath = join(postsDirectory, fileName);
return fs.readFileSync(postPath, 'utf-8');
};
export const getAllPosts = () => {
const files = fs.readdirSync(postsDirectory);
const posts = files.map((fileName) => {
const markdownWithMeta = readFile(fileName);
const { data: metadata } = matter(markdownWithMeta);
return {
metadata,
slug: fileName.split('.')[0],
};
});
return posts;
};
After that, we can use getAllPosts
api to fetch data in blog/index.tsx
. In blog/index.tsx
, we use posts as our props to render them on page.
Let's add thumbnail, title and description in every post block, and also use <Link>
component from 'next/link'
to link post by click event.
// index.tsx
import { getAllPosts } from '../../lib/api';
import Link from 'next/link'
const Blog: NextPage<Props>= (props: Props) => {{
return (
<>
{
posts.map((post, index) => <div>
<img url={post.metadata.thumbnailUrl}/>
<h2>{post.metadata.title}</h2>
<p>{post.metadata.description}</p>
<Link href={`/blog/${post.slug}`}>
<a>Read more...</a>
</Link>
</div> )
}
</>
)
}
export const getStaticProps = async () => {
const posts = getAllPosts();
return {
props: {
posts
}
}
}
In slug.tsx
, it will generate all path list at build time use by getStaticPaths
.
// [slug].tsx
export const getStaticPaths = () => {
const posts = getAllPosts();
const paths = posts.map((post) => {
return {
params: {
slug: post.slug,
},
};
});
return {
paths,
fallback: false,
};
};
Set MDX in [slug].tsx page
In the beginning, we add a new method named getPostBySlug
in lib/api.tsx
, and call it when [slug].tsx
fetch MDX files with getStaticProps
// lib/api.tsx
...
export const getPostBySlug = (slug: string) => {
const fileContents = readFile(`${slug}.mdx`);
const { data: metadata, content } = matter(fileContents);
return { metadata, content, slug };
};
...
Before calling getPostBySlug
, we need to install next-mdx-remote
which support to add MDX in Next.js app.
There is a method called serialize
in next-mdx-remote
, it parse and compile the MDX string thus rendered on page.
npm install -D next-mdx-remote
The next step is called getPostBySlug
which fetch post content and metadata in getStaticProps
, and use them as props of blog/[slug].txt
page.
// [slug].tsx
import { getPostBySlug } from '../../lib/api';
import matter from 'gray-matter'
export const getStaticProps = async ({ params: { slug } }: Params) => {
const { metadata, content } = getPostBySlug(slug);
const { mdxSource } = await serialize(content);
return {
props: {
metadata,
mdxSource,
},
};
};
After that, we import MDXRemote
from next-mdx-remote
, and use the <MDXRemote/>
to render MDX content.
Not only the MDX source, but we could set our custom components in the components
prop.
I use <PostThumbnail>
as following case.
// [slug].tsx
...
import { PostThumbnail } from '../../components';
const markdownComponents = {PostThumbnail}
const Post = ({ mdxSource, prevPost, nextPost }: Props) => (
<MDXRemote {...mdxSource} components={markdownComponents}/>
)
...
export default Post;
There is a basic content shown in page. Because we also get metadata from getPostBySlug
,
we could use thumbnailUrl
as our post banner or showing any information.
Now, we have a basic blog with features of listing all posts and clicking to link any post content.
Next time, I'll implement how to use tailwind CSS beautifying our blog as shown below in the figure, tailwind is a CSS library based on atom class. It's a new way to write style but systematic and with good performance.

See you next time ! :-)