svg-module
svg-module copied to clipboard
Not work on nuxt 3
Thanks @dacoto97 for reporting this. We have to support Vite. Maybe we should use packages like https://github.com/visualfanatic/vite-svg or https://github.com/jpkleemans/vite-svg-loader Related to:
- https://github.com/JetBrains/svg-sprite-loader/issues/434
- https://github.com/visualfanatic/vue-svg-loader/issues/142
Solution:
npm install vite-svg-loader --save-dev
import { defineNuxtConfig } from "nuxt3"
import svgLoader from "vite-svg-loader"
export default defineNuxtConfig({
vite: {
plugins: [svgLoader()]
}
})
I reopen the issue because we need to add the support for Vite to this module
@manuelodelain anything new on this?
Hi @niklasfjeldberg Unfortunately not ! I would like to do something really soon but had no time to check Vite / Nuxt3. Any help would be appreciated.
@dacoto97
This solution appears to break Nuxt 3's asset resolution
eg <img src="~/assets/icons/icon.svg" />
Have you found a workaround for this ?
Solution:
npm install vite-svg-loader --save-dev
import { defineNuxtConfig } from "nuxt3" import svgLoader from "vite-svg-loader" export default defineNuxtConfig({ vite: { plugins: [svgLoader()] } })
I appreciate you sharing this! However the problem with this solution is that vite-svg-loader does not work with "~" paths, meaning you need to rewrite all your SVG paths to "../assets/icon.svg" pattern. Sounds a bit limiting when you want to keep using location-independent paths in your project
In Nuxt 3, you don't need vite-svg-loader
, but can instead create a custom component utilizing Vite's glob import:
<template>
<span v-if="icon" class="h-[1em] w-[1em]" v-html="icon" />
</template>
<script setup lang="ts">
const props = defineProps<{
name?: string
}>()
// Auto-load icons
const icons = Object.fromEntries(
Object.entries(import.meta.glob('~/assets/images/*.svg', { as: 'raw' })).map(
([key, value]) => {
const filename = key.split('/').pop()!.split('.').shift()
return [filename, value]
},
),
)
// Lazily load the icon
const icon = props.name && (await icons?.[props.name]?.())
</script>
In Nuxt 3, you don't need
vite-svg-loader
, but can instead create a custom component utilizing Vite's glob import:<template> <span v-if="icon" class="h-[1em] w-[1em]" v-html="icon" /> </template> <script setup lang="ts"> const props = defineProps<{ name?: string }>() // Auto-load icons const icons = Object.fromEntries( Object.entries(import.meta.glob('~/assets/images/*.svg', { as: 'raw' })).map( ([key, value]) => { const filename = key.split('/').pop()!.split('.').shift() return [filename, value] }, ), ) // Lazily load the icon const icon = props.name && (await icons?.[props.name]?.()) </script>
I wish I knew that before... Although vite-svg-loader offers SVGO optimization, is there any way to use svgo with vite glob import?
Nope, no SVGO for now. But I will create a module for that.
In Nuxt 3, you don't need
vite-svg-loader
, but can instead create a custom component utilizing Vite's glob import:<template> <span v-if="icon" class="h-[1em] w-[1em]" v-html="icon" /> </template> <script setup lang="ts"> const props = defineProps<{ name?: string }>() // Auto-load icons const icons = Object.fromEntries( Object.entries(import.meta.glob('~/assets/images/*.svg', { as: 'raw' })).map( ([key, value]) => { const filename = key.split('/').pop()!.split('.').shift() return [filename, value] }, ), ) // Lazily load the icon const icon = props.name && (await icons?.[props.name]?.()) </script>
Your solution is super interesting but I am still a bit confused. Is there a way to avoid the span
in this situation and import svg
as pure component
? I would like to pass some class to the svg
itself and it's kind of annoying having this span
wrapping it. Also using v-html
can be dangerous in some situation and I would prefer avoiding it but I don't see another way to go with your solution.
@BenjaminOddou You should be able to get rid of the span by generating a VNode.
Something like (untested):
const VNode = () => h('svg', { innerHTML: /* your content */ })
Hello @madc and thank you very much for your response. I finally succeeded to get rid of the span
with vite-svg-loader
by creating a "SVG component" :
<!-- TheSVG.vue -->
<script setup lang="ts">
const props = defineProps<{ name: string }>()
const icon = defineAsyncComponent(() => import(`../assets/svgs/${props.name}.svg?component`))
</script>
<template>
<component :is="icon" />
</template>
<!-- SomePage.vue -->
<template>
<TheSVG id="big-circle" name="big-circle" />
</template>
but I still struggle to interact with inline elements inside svg when they are imported. In a nutshell, I remarked that my inner svg elements aren't accessible onMounted
(querySelector
returns null
after NuxtLink
but working on hard reload).
I created a discussion about it here. Don't hesitate to leave a message or suggestion 😃
I defined these two lines in the main.js file
import logo from './public/vite.svg'
<img src="${logo}" class="logo" alt="Vite logo" />
Then I ran npm run build
and the icon is displayed
I defined these two lines in the main.js file
import logo from './public/vite.svg' <img src="${logo}" class="logo" alt="Vite logo" />
Then I ran npm run build
and the icon is displayed
An alternative to this solution that does not throw an error:
// you can get the path or filename from props or whatever
const logo = "/_nuxt/public/logo.svg"
<img :src="`${logo}`" class="logo" alt="Vite logo" />
use vite-svg-loader but don't foget to pass 'raw' as defaultImport
svgLoader({
defaultImport: 'raw',
})