How to create a blog for your Next.js and ChakraUI website
Tags: nextjs, webdev, react, tutorial
Published: 2021-07-31 12:04:58

I have been writing on [dev.to](https://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](https://my-portfolio-ma-ahmad.vercel.app). 
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](https://my-portfolio-ma-ahmad.vercel.app/blog)
Github repo: [https://github.com/MA-Ahmad/myPortfolio](https://github.com/MA-Ahmad/myPortfolio)

### 1. Packages required
* [prismjs](https://www.npmjs.com/package/prismjs) - Syntax highlighting
* [remark-prism](https://www.npmjs.com/package/remark-prism) - Syntax highlighter for markdown code blocks
* [gray-matter](https://www.npmjs.com/package/gray-matter) - Load the frontmatter
* [remark](https://www.npmjs.com/package/remark) - Unified processor to parse and serialize Markdown
* [remark-html](https://www.npmjs.com/package/remark-html) - Serialize Markdown as HTML

### 2. Create a mdx file
See mdx file sample here [mdx-file](https://github.com/MA-Ahmad/myPortfolio/blob/main/data/posts/how-to-create-a-blog-for-your-nextjs-portfolio-website.mdx)

### 3. Create a [Blog](https://github.com/MA-Ahmad/myPortfolio/blob/main/pages/blog.tsx) page
Show all local and dev.to posts

```ts
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](https://github.com/MA-Ahmad/myPortfolio/blob/main/pages/blog/%5Bslug%5D.tsx) page

* Get paths of all blog posts
```ts
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
```ts
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
```ts
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
  };
};
```

### 5. Create a [custom stylesheet](https://github.com/MA-Ahmad/myPortfolio/blob/main/components/ui/prism.tsx) to handle dark and light theme 

## Blog page
![blog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dsvqtm2b7gxter0x7jt9.png)