reactjsnext.jsnext.js13nextjs-imagenextjs-dynamic-routing

Problem to display images in the browser in Nextjs that are not stored in /public folder?


I am working with Next.js as a full-stack framework, using API route handlers in the backend.

I have stored the images in a Post method in the /uploads directory using the fs.writeFile method, and then I stored only the path to the image. This works fine I can see the image, but when I try to call it in the browser, I can't. I have tried to use and but with no success; I only get 404, 400, and in the terminal console: [0] The requested resource isn't a valid image for /uploads/images/43820915700090.jpg received text/html; charset=utf-8.

If I store the images under the /public folder, it works, but I have heard that the public folder only compiles during the build process and only serves for static content like icons and static images.

I know that I can use services like Cloudinary (I have used before) or S3 to give me a public url and then use the image , but the client doesn't want that. What should I do?


Solution

  • I have found the solution:

    Next.js only uses the images that are stored in the public folder. However, the problem is that this folder serves the static content, so in the build process, it will copy /public into /.next/public, and I can't serve dynamic images from there.

    To display dynamic paths that are stored in the database, I have used the fs:node utility to read the image in a React component, and then called it in page.jsx as shown below:

    import fs from 'fs';
    import path from 'path';
        
    const MyImageComponent = ({ imageName, alt, width, height }) => {
          let startTime = performance.now()
          const imagePath = path.join(process.cwd(),  imageName);
        
          try {
            const ext = path.extname(imagePath).slice(1);
            console.log(ext)
            const image = fs.readFileSync(imagePath, 'base64');
            const imageUrl = `data:image/${ext};base64,${image}`;
            let endTime = performance.now()
        
            console.log(`reading the image took ${endTime - startTime} milliseconds`) // to test the time takes to render the image ===> between 0.2xx milliseconds and 1,xx second.
            
            return <img src={imageUrl} alt={alt} width={width} height={height} />;
          } catch (error) {
            console.error('Error reading image file:', error);
            return null;
          }
        };
    
    export default MyImageComponent;
    

    and in page.jsx I called it :

    export default function Home(){ 
      return (
        <MyImageComponent  imageName ="./app/images/21356888.png" />
      )
    }