N
Nuxt2d ago
Wardan

Question about image tag and svgs in production

Hello I am working in Nuxt3 and am fairly new to web dev and very new to Nuxt3 I am trying to learn how to properly use a svg that will work in production. Here is the code that was in my page before moving to a component, it worked in dev and production
<span
class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base"
>
<img
src="~/assets/images/icons/person.svg"
alt="Person"
class="mr-2 w-3.5 md:w-auto"
/>
Name
</span>
<span
class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base"
>
<img
src="~/assets/images/icons/person.svg"
alt="Person"
class="mr-2 w-3.5 md:w-auto"
/>
Name
</span>
I have many of these so I moved it to a component, once moving it to a component the svg's broke and no longer loaded. so I just changed it to a more static URL (instead of ~/assets) as shown below However, this only works in dev and not production
<script setup lang="ts">
interface Props {
imgSrc: string;
text: string;
}
defineProps<Props>();
</script>
<template>
<span
class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base"
>
<img :src="`/_nuxt/assets/images/icons/${imgSrc}`" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>
<script setup lang="ts">
interface Props {
imgSrc: string;
text: string;
}
defineProps<Props>();
</script>
<template>
<span
class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base"
>
<img :src="`/_nuxt/assets/images/icons/${imgSrc}`" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>
I then tried to changed the img src to
"`~/assets/images/icons/${imgSrc}`"
"`~/assets/images/icons/${imgSrc}`"
just to try it in production (or dev) but it did not work and it would show that path if I inspected I googled and search for a solution and did not find one. maybe I am not understanding something properly I tried this chatgpt solution
<script setup lang="ts">
interface Props {
imgSrc: string;
text: string;
}

defineProps<Props>();

// Dynamically resolve the image
const resolveImg = (imgSrc: string) => {
return new URL(`~/assets/images/icons/${imgSrc}`, import.meta.url).href;
};
</script>

<template>
<span
class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base"
>
<img :src="resolveImg(imgSrc)" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>
<script setup lang="ts">
interface Props {
imgSrc: string;
text: string;
}

defineProps<Props>();

// Dynamically resolve the image
const resolveImg = (imgSrc: string) => {
return new URL(`~/assets/images/icons/${imgSrc}`, import.meta.url).href;
};
</script>

<template>
<span
class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base"
>
<img :src="resolveImg(imgSrc)" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>
It worked in dev but not production, when inspecting I'd get this URL
<img src="file:///var/www/SITENAME/live/nuxt_app/.output/server/assets/images/icons/home.svg" alt="Address" class="mr-2 w-3.5 md:w-auto">
<img src="file:///var/www/SITENAME/live/nuxt_app/.output/server/assets/images/icons/home.svg" alt="Address" class="mr-2 w-3.5 md:w-auto">
I then tried a second chat gpt solution which works, but feels very hacky and I'd like to learn how to properly handle this The second working chatgpt solution was to create a map of the images and use them in the component via the key
import attachMoney from '~/assets/images/icons/attach_money.svg';

export const icons = {
attachMoney,
... // more values
};
import attachMoney from '~/assets/images/icons/attach_money.svg';

export const icons = {
attachMoney,
... // more values
};
I have not tried sticking the svg's in the public folder but I also do want them to accessible at a static URL which is why I havent tried this. Some insight/explanation on how to properly do this would be fantastic and appreciated.
7 Replies
kapa.ai
kapa.ai2d ago
Hi @Wardan. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To properly handle SVG images in Nuxt 3, especially when moving from direct usage to components and ensuring they work in both development and production environments, there are a few approaches you can consider. Let's go through the options and best practices: 1. Using the @nuxt/image module: The @nuxt/image module provides an easy way to handle images in Nuxt 3. It offers the <NuxtImg> component which can resolve image paths correctly in both development and production. First, make sure you have the @nuxt/image module installed and configured in your nuxt.config.ts file:
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
dir: 'assets/images'
}
})
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
dir: 'assets/images'
}
})
Then, in your component, you can use the <NuxtImg> component like this:
<template>
<span class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base">
<NuxtImg :src="`/icons/${imgSrc}`" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>

<script setup lang="ts">
interface Props {
imgSrc: string;
text: string;
}
defineProps<Props>();
</script>
<template>
<span class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base">
<NuxtImg :src="`/icons/${imgSrc}`" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>

<script setup lang="ts">
interface Props {
imgSrc: string;
text: string;
}
defineProps<Props>();
</script>
This approach should work correctly in both development and production environments Stack Overflow: NuxtImage returning 404 (https://stackoverflow.com/questions/77730500). 2. Using the public folder: If you want your SVGs to be accessible at a static URL, you can place them in the public folder. Files in this folder are served at the root path and are not processed by Webpack. This approach works well for assets that don't need processing or optimization. Move your SVGs to public/images/icons/, and then use them in your component like this:
<template>
<span class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base">
<img :src="`/images/icons/${imgSrc}`" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>
<template>
<span class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base">
<img :src="`/images/icons/${imgSrc}`" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>
This approach ensures that your SVGs are accessible at a static URL and will work in both development and production. 3. Using the assets folder with proper import: If you prefer to keep your SVGs in the assets folder for processing, you can import them directly in your component:
<template>
<span class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base">
<img :src="resolvedSrc" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>

<script setup lang="ts">
import { computed } from 'vue';

interface Props {
imgSrc: string;
text: string;
}
const props = defineProps<Props>();

const resolvedSrc = computed(() => {
return new URL(`../assets/images/icons/${props.imgSrc}`, import.meta.url).href;
});
</script>
<template>
<span class="inline-flex items-center rounded-full border border-primary-300 bg-card-fill px-4 py-3 text-xs font-semibold text-highlight backdrop-blur-sm md:text-base">
<img :src="resolvedSrc" :alt="text" class="mr-2 w-3.5 md:w-auto" />
<p>{{ text }}</p>
</span>
</template>

<script setup lang="ts">
import { computed } from 'vue';

interface Props {
imgSrc: string;
text: string;
}
const props = defineProps<Props>();

const resolvedSrc = computed(() => {
return new URL(`../assets/images/icons/${props.imgSrc}`, import.meta.url).href;
});
</script>
This approach dynamically imports the SVG based on the imgSrc prop and should work in both development and production Stack Overflow: Why does Nuxt3 not resolve URLs passed as props to a Vue component? (https://stackoverflow.com/questions/77728394). Remember that when using the assets folder, Nuxt will process and optimize these files during the build process, which can be beneficial for performance. By using one of these methods, you should be able to properly handle SVGs in your Nuxt 3 application, ensuring they work correctly in both development and production environments. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2d ago
<@703789379705110528> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Wardan
WardanOP2d ago
Well using nuxtimg doesnt seem to work either, guess I'll stick with the chatgpt solution that seems a lil hacky but at least it works Still would love an explanation on why i cant use the assets folder in components without some weird solution Is it normal to throw svgs/images that are used in components in the public folder? idk this seems really weird and confusing to me
Cue
Cue2d ago
If you want to load dynamically, and you know those files will exist, then public is exactly where you want to place them. Importing assets isn’t hacky. It’s hinting to the bundler what files you’re going to be using ahead of time, because bundlers do not evaluate your dynamic URL therefore has no idea what combinations you’re going to throw at it. This is all avoidable with public files.
Wardan
WardanOP2d ago
oh okay. i guess i said its hacky cause i didnt really understand why it worked fine without imports in a page but not a component ty so when would the assets folder come in handy? what kind of stuff would i use it for?
Cue
Cue2d ago
Common media, images and font types that you’re explicitly referencing, otherwise referred to as static assets. That is, using the ~/assets/**/[file].[ext] syntax in their expected locations such as an images src attribute, a url CSS directive, so on. For example, I often put CSS files in there, and add those files to the nuxt config. Those files may also reference images for backgrounds, fonts, so on. There may be static images I’m using like a logo, etc.
Wardan
WardanOP2d ago
okay i see. thats also where i put my css and font files. glad i got that part right at least lol. tyvm for the explanation
Want results from more Discord servers?
Add your server