nuxt-2-spa-cache-fix-module
v1.1.1
Published
Fixes unbounded LRU cache in @nuxt/vue-renderer SPARenderer (Nuxt 2)
Maintainers
Readme
nuxt-2-spa-cache-fix-module
A Nuxt 2 module that fixes the unbounded LRU cache memory leak in @nuxt/vue-renderer's SPARenderer. No file patching needed — works entirely via Nuxt's hook system.
The Problem
In @nuxt/vue-renderer v2.17.3, the SPARenderer constructor creates an LRU cache with no max limit:
// packages/vue-renderer/src/renderers/spa.js#L14
this.cache = new LRU() // no max — entries are cached foreverThe cache key is built from the full request URL, including query parameters:
// packages/vue-renderer/src/renderers/spa.js#L23-L24
const { url = '/' } = renderContext
const cacheKey = `${modern ? 'modern:' : 'legacy:'}${url}`Every unique URL creates a new cache entry that is never evicted:
// packages/vue-renderer/src/renderers/spa.js#L57
this.cache.set(cacheKey, content) // stored forever, no max limitIn production, unique query strings (UTM tags, tracking params, cache busters, etc.) cause the cache to grow without bound, leading to memory leaks that eventually crash the Node.js process.
See: nuxt/nuxt#32308 | Full source: spa.js
How It Works
The module hooks into Nuxt's render:resourcesLoaded event and wraps VueRenderer.createRenderer(). After each call (including hot reloads in dev), it replaces the unbounded cache with a bounded one.
Nuxt lifecycle:
loadResources()
→ callHook('render:resourcesLoaded') ← module hooks here
→ createRenderer() ← module wraps this
→ new SPARenderer() ← unbounded cache created
→ module replaces cache with bounded LRU({ max })Installation
# npm
npm install nuxt-2-spa-cache-fix-module
# yarn
yarn add nuxt-2-spa-cache-fix-module
# pnpm
pnpm add nuxt-2-spa-cache-fix-module
# bun
bun add nuxt-2-spa-cache-fix-module
lru-cachev5 is already installed in every Nuxt 2 project as a transitive dependency of@nuxt/vue-renderer. No need to add it separately.
Usage
Module syntax (recommended)
// nuxt.config.js
export default {
modules: [
['nuxt-2-spa-cache-fix-module', { max: 100 }]
]
}With separate options
// nuxt.config.js
export default {
modules: [
'nuxt-2-spa-cache-fix-module'
],
'nuxt-2-spa-cache-fix-module': {
max: 200
}
}CommonJS (require)
// nuxt.config.js
module.exports = {
modules: [
['nuxt-2-spa-cache-fix-module', { max: 100 }]
]
}ES import
// nuxt.config.js
import spaCacheFix from 'nuxt-2-spa-cache-fix-module'
export default {
modules: [
[spaCacheFix, { max: 100 }]
]
}Options
| Option | Type | Default | Description |
|--------|--------|---------|------------------------------------------------------|
| max | Number | 100 | Maximum number of entries in the SPA renderer cache. Once the limit is reached, the least recently used entry is evicted. |
Choosing a max value
- 100 (default) — suitable for most sites with a limited number of routes
- 500–1000 — for sites with many unique pages (e-commerce catalogs, etc.)
- 50 or less — for memory-constrained environments
The right value depends on your traffic patterns. Each cache entry holds the rendered SPA HTML shell, which is typically small (a few KB).
Compatibility
- Nuxt 2.x (tested with 2.15–2.18)
- Node.js 16, 18, 20
- Works with both
mode: 'spa'andmode: 'universal'(SPA fallback)
Development
# Install dependencies
npm install
# Run tests
npm test