I have been writing on dev.to for more than 1 year. I really like to write articles on dev.to but now I wanted to write posts on my portfolio website. So I created a blog where I'll be writing new articles and showing my dev.to posts. I'll guide you how you can create similar blog for your Next.js website.
Live demo: blogs-list Github repo: https://github.com/MA-Ahmad/myPortfolio
1. Install all these packages
- Syntax highlighting - prismjs
- Syntax highlighter for markdown code blocks - remark-prism
- Load the frontmatter - gray-matter
- Unified processor to parse and serialize Markdown - remark
- Serialize Markdown as HTML - remark-html
2. Create a mdx file
See mdx file sample here mdx-file
3. Create a Blog page
Show all local and dev.to posts.
const getPosts = async () => {
const res = await fetch("https://dev.to/api/articles?username=m_ahmad");
const posts = await res.json();
return posts;
};
const root = process.cwd();
export const getStaticProps: GetStaticProps = async () => {
const paths = fs
.readdirSync(path.join(root, "data", "posts"))
.map(p => p.replace(/\.mdx/, ""));
const localPosts = [];
paths.map(p => {
const markdownWithMeta = fs.readFileSync(
path.join(root, "data", "posts", `${p}.mdx`),
"utf-8"
);
const { data: frontmatter } = matter(markdownWithMeta);
localPosts.push({
slug: p,
title: frontmatter.title,
description: frontmatter.description,
published_at: frontmatter.published_at,
comments_count: frontmatter.comments_count,
public_reactions_count: frontmatter.public_reactions_count,
tag_list: frontmatter.tags,
url: null
});
});
const devtoPosts = await getPosts();
const posts = [...localPosts, ...devtoPosts];
if (!posts) {
return {
notFound: true
};
}
return {
props: { posts },
revalidate: 1
};
};
4. Create a blog detail page
- Get paths of all blog posts
const root = process.cwd();
export const getStaticPaths: GetStaticPaths = async () => {
const devData: BlogPost[] = await getAllBlogs();
const devtoPaths = devData.map(data => ({
params: { slug: data?.slug }
}));
const localPaths = fs
.readdirSync(path.join(root, "data", "posts"))
.map(p => ({
params: {
slug: p.replace(/\.mdx/, "")
}
}));
return {
paths: [...devtoPaths, ...localPaths],
fallback: true
};
};
const getAllBlogs = async () => {
const res = await fetch("https://dev.to/api/articles?username=m_ahmad");
if (res.status < 200 || res.status >= 300) {
throw new Error(
`Error fetching... Status code: ${res.status}, ${res.statusText}`
);
}
const data = await res.json();
return data;
};
- markdown to html code
const markdownToHtml = async (markdown: string) => {
const result = await remark()
.use(html)
.use(prism)
.process(markdown);
return result.toString();
};
- Select the right blog and convert it to html
export const getStaticProps: GetStaticProps = async ({ params }) => {
const devData: BlogPost[] = await getAllBlogs();
const selectedBlog = devData.filter(data => data?.slug === params?.slug);
let blogObj = null,
remarkContent = null;
if (selectedBlog.length) {
const res = await fetch(
`https://dev.to/api/articles/${selectedBlog[0]?.id}`
);
blogObj = await res.json();
remarkContent = await markdownToHtml(blogObj.body_markdown);
} else {
const markdownWithMeta = fs.readFileSync(
path.join(root, "data", "posts", `${params?.slug}.mdx`),
"utf-8"
);
const { data: frontmatter, content } = matter(markdownWithMeta);
blogObj = frontmatter;
remarkContent = await markdownToHtml(content);
}
if (!devData) {
return {
notFound: true
};
}
return {
props: {
articleContent: remarkContent,
blogDetails: blogObj
},
revalidate: 1
};
};