Skip to content

SVG files with <symbols>/<use> don't render #2075

@Spunkie

Description

@Spunkie

Describe the bug:

I'm working on a nuxt site with uses nuxt content and in those content files I'm referencing images in a normal markdown way:

[ ![Glasses Icon](/images/icons/glasses.svg) ]

nuxt content then uses nuxt image(and by extension IPX) to transform the that into the final html:

<img data-nuxt-img="" srcset="/_ipx/_/images/icons/glasses.svg 1x, /_ipx/_/images/icons/glasses.svg 2x" onerror="this.setAttribute('data-error', 1)" alt="Glasses Icon" src="/_ipx/_/images/icons/glasses.svg" data-error="1">
  1. If I'm loading the normal glasses.svg file, everything works like normal and the svg image renders on the page.
  2. If I then go update the glasses.svg file to have <symbol>/<use> tags and then reload the page.
    • The svg image does not render, instead displaying just the alt text Glasses Icon in it's place.
    • If I go directly to the image(/_ipx/_/images/icons/glasses.svg) it returns this error json:
{
  "error": {
    "message": "[400] [IPX_INVALID_IMAGE] Cannot parse image metadata: /images/icons/glasses.svg"
  }
}

I wasn't sure how much of this bug is nuxt/image related or strictly an IPX issue so I've opened an issue on both repos.
IPX bug report: unjs/ipx#287

SVG Files:

glasses.svg

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 111.58 77.24" fill="none">
	<path fill="#222328" d="M103.25 42.35a1.6 1.6 0 0 0-.13-.36l-5.8-11.69c-1.27-2.49-4.5-6.39-8.57-5.22-4.35 1.25-4.55 5.62-4.06 7.76a1.688 1.688 0 1 0 3.29-.77c-.07-.31-.64-3.07 1.7-3.75 2.35-.68 4.61 3.47 4.62 3.49l4.04 8.14c-.16-.05-.32-.11-.48-.15-8.12-2.39-16.58.89-21.14 7.51a8.53 8.53 0 0 0-5.11-4.19 8.52 8.52 0 0 0-6.57.75c-.25-8.03-5.59-15.37-13.71-17.76-8.88-2.61-18.18 1.55-22.32 9.46L12.81 5.43C11.54 2.94 8.31-.95 4.25.21-.1 1.46-.31 5.83.19 7.97a1.688 1.688 0 1 0 3.29-.77c-.07-.31-.64-3.07 1.7-3.75 2.35-.67 4.61 3.47 4.64 3.54l17.6 32.74c-2.52 9.9 3.24 20.13 13.13 23.04 10.11 2.97 20.75-2.83 23.73-12.94.8-2.72 3.66-4.28 6.38-3.48s4.28 3.66 3.48 6.38c-2.97 10.11 2.83 20.75 12.94 23.73 10.11 2.97 20.75-2.83 23.73-12.94 2.39-8.14-.9-16.62-7.55-21.17ZM41.5 59.54C33.18 57.09 28.4 48.33 30.84 40c2.45-8.32 11.21-13.1 19.54-10.66 8.32 2.45 13.1 11.21 10.66 19.54-2.45 8.32-11.21 13.1-19.54 10.66m46.53 13.69c-8.32-2.45-13.1-11.21-10.66-19.54 2.45-8.32 11.21-13.1 19.54-10.66 8.32 2.45 13.1 11.21 10.66 19.54-2.45 8.32-11.21 13.1-19.54 10.66"/>
	<path fill="#222328" d="M50.64 32.54c-.43.18-.63.68-.45 1.11l5.82 13.8a.855.855 0 0 0 1.11.45c.43-.18.63-.68.45-1.11l-5.82-13.8a.85.85 0 0 0-1.11-.45m1.63 10.8a.84.84 0 0 0-1.12-.42.85.85 0 0 0-.42 1.12l2.16 4.8c.11.23.3.4.53.46.19.05.39.05.58-.04a.85.85 0 0 0 .42-1.12l-2.16-4.8Zm32.17 10.81a.85.85 0 0 0-1.11-.45c-.43.18-.63.68-.45 1.11l5.82 13.8a.855.855 0 0 0 1.11.45c.43-.18.63-.68.45-1.11zM82.8 59.7a.834.834 0 0 0-1.12-.42.85.85 0 0 0-.42 1.12l2.16 4.8c.11.23.3.4.53.46.19.05.39.05.58-.04a.85.85 0 0 0 .42-1.12l-2.16-4.8Z"/>
</svg>

✅ Works normally


glasses.svg but with <symbol> and a default <use> tag

<svg xmlns="http://www.w3.org/2000/svg">
	<symbol id="icon-glasses" viewBox="0 0 111.58 77.24" fill="none">
		<path fill="#222328" d="M103.25 42.35a1.6 1.6 0 0 0-.13-.36l-5.8-11.69c-1.27-2.49-4.5-6.39-8.57-5.22-4.35 1.25-4.55 5.62-4.06 7.76a1.688 1.688 0 1 0 3.29-.77c-.07-.31-.64-3.07 1.7-3.75 2.35-.68 4.61 3.47 4.62 3.49l4.04 8.14c-.16-.05-.32-.11-.48-.15-8.12-2.39-16.58.89-21.14 7.51a8.53 8.53 0 0 0-5.11-4.19 8.52 8.52 0 0 0-6.57.75c-.25-8.03-5.59-15.37-13.71-17.76-8.88-2.61-18.18 1.55-22.32 9.46L12.81 5.43C11.54 2.94 8.31-.95 4.25.21-.1 1.46-.31 5.83.19 7.97a1.688 1.688 0 1 0 3.29-.77c-.07-.31-.64-3.07 1.7-3.75 2.35-.67 4.61 3.47 4.64 3.54l17.6 32.74c-2.52 9.9 3.24 20.13 13.13 23.04 10.11 2.97 20.75-2.83 23.73-12.94.8-2.72 3.66-4.28 6.38-3.48s4.28 3.66 3.48 6.38c-2.97 10.11 2.83 20.75 12.94 23.73 10.11 2.97 20.75-2.83 23.73-12.94 2.39-8.14-.9-16.62-7.55-21.17ZM41.5 59.54C33.18 57.09 28.4 48.33 30.84 40c2.45-8.32 11.21-13.1 19.54-10.66 8.32 2.45 13.1 11.21 10.66 19.54-2.45 8.32-11.21 13.1-19.54 10.66m46.53 13.69c-8.32-2.45-13.1-11.21-10.66-19.54 2.45-8.32 11.21-13.1 19.54-10.66 8.32 2.45 13.1 11.21 10.66 19.54-2.45 8.32-11.21 13.1-19.54 10.66"/>
		<path fill="#222328" d="M50.64 32.54c-.43.18-.63.68-.45 1.11l5.82 13.8a.855.855 0 0 0 1.11.45c.43-.18.63-.68.45-1.11l-5.82-13.8a.85.85 0 0 0-1.11-.45m1.63 10.8a.84.84 0 0 0-1.12-.42.85.85 0 0 0-.42 1.12l2.16 4.8c.11.23.3.4.53.46.19.05.39.05.58-.04a.85.85 0 0 0 .42-1.12l-2.16-4.8Zm32.17 10.81a.85.85 0 0 0-1.11-.45c-.43.18-.63.68-.45 1.11l5.82 13.8a.855.855 0 0 0 1.11.45c.43-.18.63-.68.45-1.11zM82.8 59.7a.834.834 0 0 0-1.12-.42.85.85 0 0 0-.42 1.12l2.16 4.8c.11.23.3.4.53.46.19.05.39.05.58-.04a.85.85 0 0 0 .42-1.12l-2.16-4.8Z"/>
	</symbol>
	<use href="#icon-glasses"/>
</svg>

❌ IPX error

Workaround:

For now, I worked around by using ProseImg.vue to skip <NuxtImg> for svg files

<template>
	<!-- For SVG files, use regular img tag to bypass IPX -->
	<img v-if="isSvg"
		:src="src"
		:alt="alt"
		:class="class"
		:style="style"
		data-skip-ipx="true"
	/>
	<!-- For other images, use NuxtImg for optimization -->
	<NuxtImg v-else
		:src="src"
		:alt="alt"
		:class="class"
		:style="style"
 	/>
</template>

<script setup lang="ts">
const props = defineProps<{
	src: string
	alt?: string
	class?: string
	style?: string
}>()

// Check if the image is an SVG without <symbol> #id
const isSvg = computed(() => {
	return props.src?.toLowerCase().endsWith('.svg')
})
</script>

But also, for some reason, if I have this extra commented out line at the top of the glasses.svg file with <symbol>/<use> tags the IPX error goes away:

<!-- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 111.58 77.24" fill="none"> -->
<svg xmlns="http://www.w3.org/2000/svg">
	<symbol id="icon-glasses" viewBox="0 0 111.58 77.24" fill="none">
		<path fill="#222328" d="M103.25 42.35a1.6 1.6 0 0 0-.13-.36l-5.8-11.69c-1.27-2.49-4.5-6.39-8.57-5.22-4.35 1.25-4.55 5.62-4.06 7.76a1.688 1.688 0 1 0 3.29-.77c-.07-.31-.64-3.07 1.7-3.75 2.35-.68 4.61 3.47 4.62 3.49l4.04 8.14c-.16-.05-.32-.11-.48-.15-8.12-2.39-16.58.89-21.14 7.51a8.53 8.53 0 0 0-5.11-4.19 8.52 8.52 0 0 0-6.57.75c-.25-8.03-5.59-15.37-13.71-17.76-8.88-2.61-18.18 1.55-22.32 9.46L12.81 5.43C11.54 2.94 8.31-.95 4.25.21-.1 1.46-.31 5.83.19 7.97a1.688 1.688 0 1 0 3.29-.77c-.07-.31-.64-3.07 1.7-3.75 2.35-.67 4.61 3.47 4.64 3.54l17.6 32.74c-2.52 9.9 3.24 20.13 13.13 23.04 10.11 2.97 20.75-2.83 23.73-12.94.8-2.72 3.66-4.28 6.38-3.48s4.28 3.66 3.48 6.38c-2.97 10.11 2.83 20.75 12.94 23.73 10.11 2.97 20.75-2.83 23.73-12.94 2.39-8.14-.9-16.62-7.55-21.17ZM41.5 59.54C33.18 57.09 28.4 48.33 30.84 40c2.45-8.32 11.21-13.1 19.54-10.66 8.32 2.45 13.1 11.21 10.66 19.54-2.45 8.32-11.21 13.1-19.54 10.66m46.53 13.69c-8.32-2.45-13.1-11.21-10.66-19.54 2.45-8.32 11.21-13.1 19.54-10.66 8.32 2.45 13.1 11.21 10.66 19.54-2.45 8.32-11.21 13.1-19.54 10.66"/>
		<path fill="#222328" d="M50.64 32.54c-.43.18-.63.68-.45 1.11l5.82 13.8a.855.855 0 0 0 1.11.45c.43-.18.63-.68.45-1.11l-5.82-13.8a.85.85 0 0 0-1.11-.45m1.63 10.8a.84.84 0 0 0-1.12-.42.85.85 0 0 0-.42 1.12l2.16 4.8c.11.23.3.4.53.46.19.05.39.05.58-.04a.85.85 0 0 0 .42-1.12l-2.16-4.8Zm32.17 10.81a.85.85 0 0 0-1.11-.45c-.43.18-.63.68-.45 1.11l5.82 13.8a.855.855 0 0 0 1.11.45c.43-.18.63-.68.45-1.11zM82.8 59.7a.834.834 0 0 0-1.12-.42.85.85 0 0 0-.42 1.12l2.16 4.8c.11.23.3.4.53.46.19.05.39.05.58-.04a.85.85 0 0 0 .42-1.12l-2.16-4.8Z"/>
	</symbol>
	<use href="#icon-glasses"/>
</svg>

Environment

Node: v22.21.1

"devDependencies": {
	"@nuxt/content": "^3.9.0",
	"@nuxt/image": "^2.0.0",
	"nuxi": "^3.20.2",
	"nuxt": "^3.20.2",
	"nuxt-seo-utils": "^7.0.16",
	"nuxt-svgo": "^4.2.6",
	"sass": "^1.89.0",
	"sass-loader": "^16.0.6",
	"vite-svg-loader": "^5.1.0"
},
"dependencies": {
	"@nuxtjs/seo": "^3.2.2",
},
"overrides": {
	"vue": "latest"
},

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions