nuxt-icons icon indicating copy to clipboard operation
nuxt-icons copied to clipboard

nuxt-icon can cause serious memory leaks.

Open yaowangmx opened this issue 2 months ago • 2 comments

Describe the bug nuxt-icon can cause serious memory leaks.

To Reproduce

Build and run the Nuxt 3 project, then perform load testing:

  1. Build:

    pnpm build
    
  2. Run server:

    node .output/server/index.mjs
    
  3. Load test using autocannon:

    npx autocannon -c 40 -d 30 http://localhost:3000
    

1. With index.vue reduced to only a single <div>

Memory remains stable.

Test Memory Usage
Initial 25.1M
After 1st load test 37.0M
After 2nd 44.6M
After 3rd 39.7M
After 4th 40.3M
After 5th 39.9M
After 6th 40.2M
After 5 minutes 40.2M

2. Restore part of the code + enable nuxt-icons

Memory usage increases dramatically and leads to OOM after several rounds.

Test Memory Usage
Initial 20.2M
After 1st load test 1378.4M
After 2nd 2643.6M
After 3rd 3678.1M

At the 4th test, the following error appears in the console:

<--- Last few GCs --->
[30424:0000014AD5201000] 272236 ms: Mark-Compact 4014.8 (4129.0) -> 3994.8 (4124.8) MB, pooled: 0 MB, 853.34 / 0.02 ms (average mu = 0.214, current mu = 0.059) allocation failure; scavenge might not succeed
[30424:0000014AD5201000] 273138 ms: Mark-Compact 4013.8 (4128.0) -> 3993.9 (4124.0) MB, pooled: 0 MB, 869.00 / 0.01 ms (average mu = 0.131, current mu = 0.036) allocation failure; scavenge might not succeed
<--- JS stacktrace --->
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

3. Remove nuxt-icons

Memory becomes stable again.

Test Memory Usage
Initial 20.9M
After 1st load test 38.6M
After 2nd 39.9M
After 3rd 40.6M
After 4th 41.5M
After 5th 41.5M
After 6th 41.9M
After 5 minutes 41.9M

package.json { "name": "nuxt-app", "private": true, "type": "module", "scripts": { "build": "nuxt build --dotenv .env.prod", "dev": "nuxt dev --host --dotenv .env.dev" }, "dependencies": { "@element-plus/icons-vue": "^2.3.1", "@element-plus/nuxt": "^1.0.8", "@floating-ui/dom": "1.6.0", "@internationalized/date": "^3.8.1", "@nuxtjs/i18n": "^8.5.6", "@tailwindcss/vite": "^4.1.5", "@tanstack/vue-table": "^8.21.3", "@tato30/vue-pdf": "^1.11.4", "@types/file-saver": "^2.0.7", "@unhead/vue": "1.10.4", "@vee-validate/zod": "^4.15.0", "@vueuse/core": "^13.4.0", "@zadigetvoltaire/nuxt-gtm": "^0.0.13", "alfaaz": "^1.1.0", "class-variance-authority": "^0.7.1", "dayjs": "^1.11.13", "dayjs-nuxt": "2.1.11", "echarts": "^5.6.0", "element-plus": "^2.6.3", "file-saver": "^2.0.5", "js-md5": "^0.7.3", "jspdf": "2.5.1", "katex": "^0.16.22", "lodash": "^4.17.21", "lucide-vue-next": "^0.508.0", "mupdf-webviewer": "^0.9.1", "nprogress": "^0.2.0", "nuxt": "3.18.1", "pinia": "^2.2.1", "reka-ui": "2.2.1", "rollup-plugin-visualizer": "^5.14.0", "shadcn-nuxt": "^2.1.0", "spark-md5": "^3.0.2", "tailwind-merge": "^3.2.0", "tailwindcss": "^4.1.10", "tippy.js": "^6.3.7", "tw-animate-css": "^1.2.9", "vee-validate": "^4.15.0", "vite-plugin-compression": "^0.5.1", "vite-plugin-imagemin": "^0.6.1", "vue": "^3.5.16", "vue-router": "^4.5.1", "vue-sonner": "^2.0.0", "vue3-google-login": "2.0.29", "y-protocols": "^1.0.6", "yjs": "^13.6.27" }, "devDependencies": { "@iconify/utils": "^3.1.0", "@nuxtjs/tailwindcss": "^6.12.1", "@pinia-plugin-persistedstate/nuxt": "^1.2.1", "@pinia/nuxt": "^0.5.3", "@vitejs/plugin-vue-jsx": "^4.1.2", "sass": "^1.77.8", "typescript": "^5.9.2", "vite-svg-loader": "^5.1.0", "xlsx": "^0.16.1" }, "pnpm": { "overrides": { "jiti": "^2.5.1", "vite": "^7.0.6", "prosemirror-model": "1.19.0", "vue": "3.5.16", "@vue/compiler-sfc": "3.5.16", } }, "resolutions": { "vue": "3.5.16", "@vue/compiler-sfc": "3.5.16" } }

Troubleshooting Attempts

I tried multiple dependency and configuration adjustments. The following steps showed noticeable improvements and prevented the memory leak crash:

  1. Downgraded / adjusted several package versions (effective):

    nuxt: 3.12.4
    vue: 3.5.13
    @nuxtjs/i18n: ^8.3.0
    nuxt-icons: ^3.2.1
    reka-ui: 2.0.0
    shadcn-nuxt: 2.0.0
    @pinia-plugin-persistedstate/nuxt: ^1.2.1
    
  2. Removed the vue-sonner package or commented out related code (This also reduced memory growth.)

  3. Updated nuxt.config.js according to the version changes above (Some modules required different configuration formats.)

  4. Performed load testing using:

    npx autocannon -c 40 -d 60 \
      -H "Authorization: Basic cGlwaWFkczoxMjMzMjFzdW4=" \
      https://XXXX.com
    

Results After Adjustments

The memory was successfully released during and after stress testing, and the server no longer crashed due to out-of-memory errors.

yaowangmx avatar Dec 05 '25 03:12 yaowangmx

I also encountered a memory leak issue with nuxt icons. The version is "nuxt icons": "^ 3.2.1". How did you solve it

2367076952 avatar Dec 15 '25 07:12 2367076952

I also encountered a memory leak issue with nuxt icons. The version is "nuxt icons": "^ 3.2.1". How did you solve it

I removed the nuxt-icons dependency and related configurations; I extracted the author's source code and repackaged it into a NuxtIcon.vue component. The NuxtIcon.vue code is as follows: <template> <span class="nuxt-icon" :class="{ 'nuxt-icon--fill': !filled, 'nuxt-icon--stroke': hasStroke && !filled }" v-html="icon" /> </template>

`

const props = withDefaults(defineProps<{ name: string filled?: boolean }>(), { filled: false })

const iconsImport = import.meta.glob('/assets/icons/**/*.svg', { eager: false, query: '?raw', import: 'default' })

const icon = ref('') let hasStroke = false

async function getIcon() { try { const iconPath = /assets/icons/${props.name}.svg if (!iconsImport[iconPath]) { console.error([nuxt-icons] Icon '${props.name}' doesn't exist in 'assets/icons') icon.value = '' return } const rawIcon = await iconsImporticonPath as string if (rawIcon.includes('stroke')) { hasStroke = true } icon.value = rawIcon } catch (error) { console.error([nuxt-icons] Failed to load icon '${props.name}':, error) icon.value = '' } }

await getIcon()

watchEffect(getIcon) `

`

.nuxt-icon svg { width: 1em; height: 1em; margin-bottom: 0.125em; vertical-align: middle; }

.nuxt-icon.nuxt-icon--fill, .nuxt-icon.nuxt-icon--fill * { fill: currentColor !important; }

.nuxt-icon.nuxt-icon--stroke, .nuxt-icon.nuxt-icon--stroke * { stroke: currentColor !important; } `

yaowangmx avatar Dec 17 '25 03:12 yaowangmx