image icon indicating copy to clipboard operation
image copied to clipboard

Support for assets images (solution included)

Open alexbidenko opened this issue 1 year ago • 1 comments

Problem

By default, nuxt/image does not work with images imported from the assets directory, neither locally nor in production. However, image imports are crucial for building a quality website.

Imported images are processed by the build system, and a hash is assigned to them upon copying. A hash in the image filename is essential for proper caching mechanisms, both on the browser side and the web server side. Otherwise, if a file changes but retains the same name, users will not receive the updated content. Another benefit of importing images is the ability to work with various Vite plugins (which is also relevant in my case). Others might have additional reasons.

Currently, nuxt/image strongly resists using imported images from assets:

  • Locally, it won’t find images because they reside under the /_nuxt/ path, which is served by the Nuxt server. There is no corresponding folder in the public directory.
  • In production, the /_nuxt directory is created inside public, so server-side rendering works, and the <NuxtImg> component even seems to provide correct paths to the browser. But during hydration, the relative link is replaced by an absolute URL including the domain name, causing NuxtImg to stop processing it and fall back to a non-optimized version.

Solution

I found a way to solve these issues and make nuxt/image work with the assets directory. This solution works well for me and is quite straightforward.

In the component, I simply import images from assets:

<script lang="ts" setup>
import image from '~/assets/image.jpg';
</script>

<template>
  <NuxtImg :src="image" format="webp" />
</template>

To make Nuxt locally serve the processed images, I add the corresponding alias in the Nuxt Image configuration:

export default defineNuxtConfig({
  modules: ['@nuxt/image'],
  image: {
    alias: { '_nuxt/assets': 'http://localhost:3000/_nuxt/assets' },
    domains: ['http://localhost:3000'],
  },
});

If needed, you can limit these settings to only apply in development mode.

To address the domain replacement issue after the build, I extend the existing ipx provider by adding my own custom logic:

export default defineNuxtConfig({
  modules: ['@nuxt/image'],
  image: {
    providers: {
      ipx: {
        name: 'ipx',
        provider: '~/image-provider.ts',
      },
    },
    alias: { '_nuxt/assets': 'http://localhost:3000/_nuxt/assets' },
    domains: ['http://localhost:3000'],
  },
});

And in the image-provider.ts file:

import type { ProviderGetImage } from '@nuxt/image';

import { getImage as ipxProvider } from '#image/providers/ipx';

export const getImage: ProviderGetImage = (src, options, ctx) => {
  if (/^https?:\/\//.test(src))
    src = src.replace(useRuntimeConfig().public.origin, '');

  return ipxProvider(src, options, ctx);
};

Here, the public.origin config variable stores the site’s domain, but you can use any other approach to remove the domain portion.

Altogether, this method achieves the desired result, and nuxt/image works perfectly with files imported from the assets directory.

Proposal

The solution I described above is not complicated, yet it provides the significant advantages mentioned earlier. I believe such behavior should be included in nuxt/image by default.

Let _nuxt/assets be used by Nuxt during local development, and it could be automatically configured in the alias by default (taking the --host and --port parameters into account). In production, we could check in the browser environment: if the image’s domain matches the site’s domain, strip it out. (This logic could be hidden behind a provider configuration option.)

All in all, this would introduce a fantastic new feature in the nuxt/image module.

What do you think about all this?

alexbidenko avatar Dec 27 '24 11:12 alexbidenko

im looking for methods which can extract from assets similar to vue's <img src="..." /> behavior

possibly can be modified to tap into @vue/compiler-sfc via vite-plugin-vue to look for NuxtImg and compile asset URL dynamically

https://github.com/vuejs/core/blob/ba391f5fdf5d84bfacaca6a2a3e7057fc99efa34/packages/compiler-sfc/src/template/transformAssetUrl.ts#L37-L47

wuiyang avatar Jun 25 '25 17:06 wuiyang