serraosays
serraosays

Reputation: 7869

Encoding static imagery from public folder using getStaticProps in Next.js

Next.js lays out a pretty comprehensive way to get imagery from the /public/ folder (where the app has you store static assets). The pattern is to use fs from Node and do the fetch in getStaticProps.

My attempt:

export async function getStaticProps({ params, preview = false, previewData }) {
  const cityData = dataFiltered.find( city => city.citySlug === params.slug )
  const cityMapImagePath = path.join(process.cwd(), `/public/static-maps/${cityData.imgPath}`)
  const cityMapImageRes = await fs.readFile(cityMapImagePath)
  const cityMapImageProcessed = JSON.stringify(cityMapImageRes)

  return {
    props: {
      preview,
      cityData: cityData,
      cityMapImage: cityMapImageProcessed
    },
    revalidate: 60,
  };
}

This code works, but it returns a pretty weird response when I reference that object in my component:

<img src="{ "type":"Buffer", "data":[255,216,255,224,0,6,75,56,86,87,...] } />

My error has something to do with how I'm processing what fs gives me back. Do I need to encode my jpeg into base64 to get Next to use it? This answer suggests stringifying and then parsing (didn't work for me). Or maybe I need a full blown endpoint to do this? Next isn't very clear on how to get imagery from getStaticProps into the component above it - perhaps you know how?

Upvotes: 3

Views: 1846

Answers (2)

serraosays
serraosays

Reputation: 7869

What I ended up doing for the fetch in getStaticProps:

export async function getStaticProps({ params, preview = false, previewData }) {
  const cityData = dataFiltered.find( city => city.citySlug === params.slug )
  const cityMapImagePath = path.join(process.cwd(), `/public/static-maps/${cityData.imgPath}`)
  let cityMapImageRes, cityMapImageProcessed

  try {
    cityMapImageRes = await fs.readFile(cityMapImagePath)
    cityMapImageProcessed = Buffer.from(cityMapImageRes).toString('base64')
  } catch {
    cityMapImageProcessed = null
  }

  return {
    props: {
      preview,
      cityData: cityData,
      cityMapImage: cityMapImageProcessed
    },
    revalidate: 60,
  };
}

Also make sure up in the component, you are properly encoding your image source as base64 with the data:image/png;base64, prefix. This was a silly mistake that cost me an hour of debugging:

<img src={`data:image/png;base64,${cityMapImage}`} alt='Alt text here' />

Finally, also note that Next.js when used with Vercel will impose a hard, 50MB cap (compressed) on the serverless function used to process your page file request. If you're doing a [slug].js template, all the assets for EVERY page generated will count towards that cap. You'll hit it pretty fast on deploy, so double check yourself.

Update 2024-02: serverless bundle size can now go up to 250MB

Upvotes: 1

Ivan V.
Ivan V.

Reputation: 8081

All data that is returned from the getStaticProps needs to be JSON serializable, so yes, if you want to return image there , you need to base64 encode it (this can be a problem for big images). The other solution (if the scenario permits it) is not to do it with getStaticProps rather load the image on demand in the front end, by hitting the API after the page has already loaded.

Upvotes: 2

Related Questions