skip to Main Content

I am tryiing to make next js 13 mdx blog website with the latest app router
I am getting this error => Module not found: Can’t resolve ‘fs’
In next js 12 and pages directory its working but in the latest app router it’s not working

this is my code,


"use client"

import Head from 'next/head';
import BlogSidebar from '../../components/blog/sidebar';
import Pagination from '../../components/blog/pagination';
import fs from 'fs';
import matter from 'gray-matter';
import { join } from 'path';
import { useRouter } from 'next/router'


export async function getStaticProps() {
  const blogsDirectory = join(process.cwd(), '_blog');
  const files = fs.readdirSync(blogsDirectory);

  let posts = files.map((fileName) => {
    const slug = fileName.replace(/.md$/, '');
    const fullPath = join(blogsDirectory, `${slug}.md`);
    const readFile = fs.readFileSync(fullPath, 'utf8');
    const { data: frontmatter } = matter(readFile);
    return {
      slug,
      frontmatter,
      
    };
  });

  posts = posts.sort((job1, job2) => (job1.frontmatter.date > job2.frontmatter.date ? -1 : 1));

  return {
    props: {
      posts,
    },
    
  };
}


const page = ({ posts }) => {

  console.log(posts, "posts");

enter image description here

2

Answers


  1. There are several issues in the code snippet you posted.

    1. getStaticProps is not supported in Next.js 13 app router. You are going to get the following error if you try to use it in a page under the app directory:

    "getStaticProps" is not supported in app/. Read more:
    https://nextjs.org/docs/app/building-your-application/data-fetching

    You should read the above mentioned docs on data fetching in Next.js 13 app router.

    1. You declared your page as a client component, but expect it to access the server-side file system. This is just not going to work this way.

    You are going to need to rewrite your code to make it compatible with the app dir. Here is how you can approach this problem:

    import fs from "fs";
    import path from "path";
    
    async function getPosts() {
      const files = fs.readdirSync(path.resolve("_blog"));
    
      // read posts
    
      return posts;
    }
    
    export default async function Home() {
      const posts = await getPosts();
    
      console.log(posts);
    
      // render posts
    }
    

    The following migration guide is also going to be helpful.

    https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration

    Login or Signup to reply.
  2. The fs module is not available in the client-side. You can use the next/dynamic component to load the file contents on the server-side and then render them on the client-side.

    Here is the updated code:

    import Head from 'next/head';
    import BlogSidebar from '../../components/blog/sidebar';
    import Pagination from '../../components/blog/pagination';
    import matter from 'gray-matter';
    import { join } from 'path';
    import { useRouter } from 'next/router';
    
    export async function getStaticProps() {
      const blogsDirectory = join(process.cwd(), '_blog');
      const files = fs.readdirSync(blogsDirectory);
    
      let posts = files.map((fileName) => {
        const slug = fileName.replace(/.md$/, '');
        const fullPath = join(blogsDirectory, `${slug}.md`);
        const readFile = fs.readFileSync(fullPath, 'utf8');
        const { data: frontmatter } = matter(readFile);
        return {
          slug,
          frontmatter,
        };
      });
    
      posts = posts.sort((job1, job2) => (job1.frontmatter.date > job2.frontmatter.date ? -1 : 1));
    
      return {
        props: {
          posts,
        },
      };
    }
    
    const page = ({ posts }) => {
      const dynamicPost = useRouter().query.post;
      const post = posts.find(post => post.slug === dynamicPost);
    
      return (
        <div>
          <Head>
            <title>My Blog</title>
          </Head>
          <BlogSidebar />
          <main>
            {post && (
              <article>
                <h1>{post.frontmatter.title}</h1>
                <p>{post.frontmatter.content}</p>
              </article>
            )}
          </main>
          <Pagination />
        </div>
      );
    };
    
    export default page;
    

    This code will first load the list of posts on the server-side. Then, it will render the post that is specified in the query string. If there is no post specified in the query string, it will render the first post in the list.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search