Skip to content

useResizable on UDashboardSidebar crashes with "Cannot read properties of null" when persisted cookie value is null #6517

@FabianClemenz

Description

@FabianClemenz

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

  1. Mount <UDashboardSidebar collapsible> anywhere — e.g. minimal repro:

    <template>
      <UDashboardGroup>
        <UDashboardSidebar id="default" collapsible>
          <template #default>hi</template>
        </UDashboardSidebar>
        <UDashboardPanel />
      </UDashboardGroup>
    </template>
  2. In DevTools, set the cookie to the literal value null:

    document.cookie = 'dashboard-sidebar-default=null; path=/';
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageAwaiting initial review and prioritizationv4#4488

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions