Environment
- Operating system: macOS 25.4.0
- CPU: Apple M5 Pro (18 cores)
- Node.js version: v24.14.0
- nuxt/cli version: 3.34.0
- Package manager: yarn 1.22.22
- Nuxt version: 4.4.2
- Nitro version: 2.13.1
- Builder: vite 7.3.1
Is this bug related to Nuxt or Vue?
Nuxt
Package
v4.x
Version
v4.8.0
Reproduction
-
Mount <UDashboardSidebar collapsible> anywhere — e.g. minimal repro:
<template>
<UDashboardGroup>
<UDashboardSidebar id="default" collapsible>
<template #default>hi</template>
</UDashboardSidebar>
<UDashboardPanel />
</UDashboardGroup>
</template>
-
In DevTools, set the cookie to the literal value null:
document.cookie = 'dashboard-sidebar-default=null; path=/';
-
Reload. The page crashes:
TypeError: Cannot read properties of null (reading 'collapsed')
at composables/useResizable.js:27
Same effect with storage: "local" and localStorage.setItem('dashboard-sidebar-default', 'null').
Description
useResizable (used internally by UDashboardSidebar) throws on every render once the persistence cookie holds the JSON value null instead of the expected { size, collapsed } object:
TypeError: Cannot read properties of null (reading 'collapsed')
at get (composables/useResizable.js:27)
TypeError: Cannot read properties of null (reading 'size')
at get (composables/useResizable.js:40)
The errors fire on the computed getters of isCollapsed and size, so they bubble up on every reactive update of the sidebar and make the page unusable.
Additional context
In dist/runtime/composables/useResizable.js:
const storageData = opts.value.persistent && (opts.value.resizable || opts.value.collapsible)
? opts.value.storage === "cookie"
? useCookie(key, { ...opts.value.storageOptions, default: () => defaultStorageValue })
: useStorage(key, defaultStorageValue, void 0, opts.value.storageOptions)
: ref(defaultStorageValue);
const isCollapsed = computed({
get: () => storageData.value.collapsed, // ← throws if storageData.value === null
set: (value) => { /* … */ storageData.value.collapsed = value; },
});
const size = computed({
get: () => storageData.value.size, // ← throws if storageData.value === null
set: (value) => { storageData.value.size = value; },
});
Nuxt's useCookie parses cookie values with destr. The default option only fires when the cookie is undefined, not when it parses to null. Any of the following puts the ref into the null state and crashes the sidebar permanently for that user:
- Cookie header literally
dashboard-sidebar-default=null (manual edit, browser extension, older version that wrote null, server-side cookie injection, etc.)
useStorage reading a localStorage entry containing the string "null"
- A consumer writing
storageData.value = null (no guard at the boundary)
There is no defensive null check anywhere in the composable. Lines 180 and 188 access storageData.value.collapsed unguarded as well.
Environment
Is this bug related to Nuxt or Vue?
Nuxt
Package
v4.x
Version
v4.8.0
Reproduction
Mount
<UDashboardSidebar collapsible>anywhere — e.g. minimal repro:In DevTools, set the cookie to the literal value
null:Reload. The page crashes:
Same effect with
storage: "local"andlocalStorage.setItem('dashboard-sidebar-default', 'null').Description
useResizable(used internally byUDashboardSidebar) throws on every render once the persistence cookie holds the JSON valuenullinstead of the expected{ size, collapsed }object:The errors fire on the computed getters of
isCollapsedandsize, so they bubble up on every reactive update of the sidebar and make the page unusable.Additional context
In
dist/runtime/composables/useResizable.js:Nuxt's
useCookieparses cookie values withdestr. Thedefaultoption only fires when the cookie is undefined, not when it parses tonull. Any of the following puts the ref into thenullstate and crashes the sidebar permanently for that user:dashboard-sidebar-default=null(manual edit, browser extension, older version that wrotenull, server-side cookie injection, etc.)useStoragereading alocalStorageentry containing the string"null"storageData.value = null(no guard at the boundary)There is no defensive null check anywhere in the composable. Lines 180 and 188 access
storageData.value.collapsedunguarded as well.