DEV Community

Cover image for How to create a blog for your Next.js and ChakraUI website
Muhammad  Ahmad
Muhammad Ahmad

Posted on • Updated on • Originally published at my-portfolio-ma-ahmad.vercel.app

How to create a blog for your Next.js and ChakraUI website

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

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
  };
};
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode
  • markdown to html code
const markdownToHtml = async (markdown: string) => {
  const result = await remark()
    .use(html)
    .use(prism)
    .process(markdown);
  return result.toString();
};
Enter fullscreen mode Exit fullscreen mode
  • 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
  };
};
Enter fullscreen mode Exit fullscreen mode

5. Create a custom stylesheet to handle dark and light theme

Blog page

blog

Top comments (3)

Collapse
 
aligerm profile image
Ali

I love animations.

Collapse
 
alexweininger profile image
Alex Weininger

Your portfolio is amazing! I really love the open source page.

Collapse
 
m_ahmad profile image
Muhammad Ahmad

Glad you liked it.