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: posts-list
Github repo: https://github.com/MA-Ahmad/myPortfolio
1. Packages required
- prismjs - Syntax highlighting
- remark-prism - Syntax highlighter for markdown code blocks
- gray-matter - Load the frontmatter
- remark - Unified processor to parse and serialize Markdown
- remark-html - Serialize Markdown as 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 }; };
Top comments (3)
I love animations.
Your portfolio is amazing! I really love the open source page.
Glad you liked it.