image icon indicating copy to clipboard operation
image copied to clipboard

Images inside <template [tag]> are not generated during nuxt generate, but work in development mode

Open ghost opened this issue 1 year ago • 7 comments

I have encountered a problem with the @nuxt/image module when using it inside a <template> tag. Images are not generated correctly when running nuxt generate, although they work fine in development mode ( nuxt dev ).

<UPopover :popper="{ placement: 'bottom-end' }">
   <UButton icon="bi:globe-americas"/>
  
    <template #panel>
      <NuxtImg
        src="/img/example.webp"
        width="25"
        format="webp"
        densities="x1 x2"
        />
  </template>
</UPopover>

When running nuxt dev - the image will be displayed correctly. When I run nuxt generate and try to open the page, the image is not generated and the browser shows a 404 error http://localhost:3000/_ipx/f_webp/img/example.webp

<template #panel>

Expected behavior: The image should be generated and available during static generation, similar to how it works in development mode.

The image is not generated during static site generation and the page shows a 404 error when trying to load the image. However, the image works fine when running nuxt dev.

package.json

 "dependencies": {
 "@nuxt/ui": "^2.19.2",
 "@nuxtjs/i18n": "^9.0.0",
 "axios": "^1.7.7",
 "lodash": "^4.17.21",
 "nuxt": "^3.14.159",
 "nuxt-zod-i18n": "^1.10.0",
 "vue": "latest",
 "vue-router": "latest",
 "zod": "^3.23.8"
 },
 "devDependencies": {
 "@nuxt/image": "^1.8.1",
 "nuxt-aos": "^1.2.5",
 "nuxt-swiper": "^2.0.0",
 "sass-embedded": "^1.81.0"
}

Additional information: This issue seems to be related specifically to the use of NuxtImg inside a

The images work fine in development mode, but are not generated during static build.

I have verified that the images are correctly placed.

When I remove the slot ( without #panel), the image is generated correctly during static build. But adding #panel or any other gives an error.

ghost avatar Nov 22 '24 11:11 ghost

Did you manage to solve the issue?

RodrigoSpinelli avatar Nov 26 '24 12:11 RodrigoSpinelli

Did you manage to solve the issue?

no (

w33bvGL avatar Dec 09 '24 19:12 w33bvGL

[!NOTE] Please see here for a better solution

I wrote a utility to manually collect all possible images:

interface ImageOptions {
  width: number
  height: number
  sizes?: string
}

export function ensureGenImages(urls: string[], opts?: ImageOptions) {
  if (!import.meta.server)
    return
  const img = useImage()
  const { width, height, sizes } = opts || {}
  urls.forEach((url) => {
    if (opts) {
      const srcset = img.getSizes(url, { modifiers: { width, height }, sizes })
      // we don't need this in deed, see another comment below
      extractUrls(srcset.srcset).forEach((url) => {
        $fetch(url)
      })
    }
    else {
      // we don't need the `$fetch` call in deed, see another comment below
      $fetch(img(url))
    }
  })
}

function extractUrls(srcset: string) {
  return srcset.split(',').map(s => s.trim().split(' ')[0])
}

The fetch will cause ipx to generate images...

The not graceful place is that you need to extract the imageOptions(width,height,sizes) or double writing code. But I didn't find a way to avoid this...

And you need to call this utility function at somewhere always render.

a1mersnow avatar Dec 30 '24 05:12 a1mersnow

It's already a solution, thanks for sharing.

RodrigoSpinelli avatar Dec 30 '24 11:12 RodrigoSpinelli

I found that practically the $fetch call has no effect. What make this work are img call and img.getSizes call, so a more simple code:

interface ImageOptions {
  width: number
  height: number
  sizes?: string
}

export function ensureGenImages(urls: string[], opts?: ImageOptions) {
  if (!import.meta.server || !import.meta.prerender)
    return
  const img = useImage()
  const { width, height, sizes } = opts || {}
  urls.forEach((url) => {
    if (opts) {
      img.getSizes(url, { modifiers: { width, height }, sizes })
    }
    else {
      img(url)
    }
  })
}

a1mersnow avatar Dec 31 '24 09:12 a1mersnow

It's already a solution, thanks for sharing.

w33bvGL avatar Jan 01 '25 08:01 w33bvGL

已经有解决方案了,谢谢分享。

请问是什么解决方案,谢谢

foolish-pup avatar Aug 18 '25 02:08 foolish-pup