Skip to content

feat(nuxt): add reset to default functionality for useState and clearNuxtState#33527

Merged
danielroe merged 14 commits intonuxt:mainfrom
Harm-Nullix:reset-to-init-useState
Feb 25, 2026
Merged

feat(nuxt): add reset to default functionality for useState and clearNuxtState#33527
danielroe merged 14 commits intonuxt:mainfrom
Harm-Nullix:reset-to-init-useState

Conversation

@Harm-Nullix
Copy link
Copy Markdown
Contributor

@Harm-Nullix Harm-Nullix commented Oct 20, 2025

  • Refactor useState and clearNuxtState to improve statemanagement and reset functionality, introduce _state for internal use.

🔗 Linked issue

resolves #32117

📚 Description

The setup for this PR is a starting point with a proposed solution
It does break the current logic as it does not automatically sets the state to undefined but resets it to the given init function

Other paths considered:

  1. make the init parameter a options object (fallback to old init param),
    • to define a default value alongside the init value
    • or to flag the state to reset / not reset to undefined
  2. flip the flag that is now given to clearNuxtState to keep current logic as is.
  3. Setting a object in the state.ts file that holds the init functions by key rather than using nuxtApp._state

  1. I did not want to do this because it makes the function of useState less clear
  2. The solution and logic way of thinking resetting to a default value instead of undefined seemed more in line with Nuxt composables like useAsyncData
  3. I did this because it is more in line with other data objects and maybe clears the path for further improvements for the state across the whole NuxtApp.

TODO

When a solid path is determined, I will add guiding docs to the current functionality

… management and reset functionality, introduce `_state` for internal use.
@bolt-new-by-stackblitz
Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Oct 20, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This pull request refactors Nuxt's state composable by introducing InitOption<T> and UseStateReturn<T> types, updating useState overloads to accept an InitOption and return a consistent ref type, and reworking hydration to store per-key default functions and internal _state entries. Initial values are resolved via a new toValueWithFallback helper. clearNuxtState gains an optional reset parameter and per-key clearing is factored into clearNuxtStateByKey. The Nuxt app interface and createNuxtApp now include an internal _state shallowReactive map.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Type scaffolding and signature changes: Review the new InitOption<T> and UseStateReturn<T> type definitions and their impact on the useState overloads; verify backward compatibility with existing single-parameter usage
  • Hydration and lazy resolution logic: Carefully examine the reworked hydration mechanism, particularly how defaultFn is stored and resolved via toValueWithFallback, ensuring it maintains correct SSR behaviour and reference integrity
  • Reset parameter semantics: Verify that the new reset parameter in clearNuxtState correctly implements the distinction between clearing and resetting, and that it aligns with existing useAsyncData patterns
  • State synchronisation edge cases: Review test additions covering key collisions and shared keys to understand the expected interaction patterns
  • Internal _state field integration: Confirm that the new _state field on _NuxtApp integrates correctly with existing state management and SSR payload handling
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding reset-to-default functionality to useState and clearNuxtState, which aligns with the refactored state management approach.
Description check ✅ Passed The pull request description clearly relates to the changeset, detailing refactoring of useState and clearNuxtState with internal _state introduction and reset functionality changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/nuxt/src/app/composables/state.ts (1)

65-76: Safer key derivation and leaner payload clearing

  • _allKeys should only include $s-prefixed entries; otherwise non‑useState keys (if any) would be transformed and re‑prefixed, causing unintended clears.
  • When removing without reset, prefer deleting the key to avoid retaining enumerable undefined slots (keeps payload slim and prevents stale keys from reappearing in subsequent clears).

Apply:

-  const _allKeys = Object.keys(nuxtApp.payload.state)
-    .map(key => key.substring(useStateKeyPrefix.length))
+  const _allKeys = Object.keys(nuxtApp.payload.state)
+    .filter(key => key.startsWith(useStateKeyPrefix))
+    .map(key => key.substring(useStateKeyPrefix.length))

   for (const _key of _keys) {
-    clearNuxtStateByKey(nuxtApp, useStateKeyPrefix + _key, reset ?? true)
+    clearNuxtStateByKey(nuxtApp, useStateKeyPrefix + _key, reset ?? true)
   }

And streamline per‑key clearing:

-function clearNuxtStateByKey (nuxtApp: NuxtApp, key: string, reset: boolean): void {
-  if (key in nuxtApp.payload.state) {
-    nuxtApp.payload.state[key] = undefined
-  }
-
-  if (nuxtApp._state[key]) {
-    nuxtApp._state[key]!.data.value = reset ? unref(nuxtApp._state[key]!._default()) : undefined
-  }
-}
+function clearNuxtStateByKey (nuxtApp: NuxtApp, key: string, reset: boolean): void {
+  if (reset) {
+    if (nuxtApp._state[key]) {
+      // Resets both _state data and payload via toRef linkage
+      nuxtApp._state[key]!.data.value = unref(nuxtApp._state[key]!._default())
+      return
+    }
+    // No _state registered: remove from payload if present
+    delete nuxtApp.payload.state[key]
+  } else {
+    // Remove from payload and null out internal value if tracked
+    delete nuxtApp.payload.state[key]
+    if (nuxtApp._state[key]) {
+      nuxtApp._state[key]!.data.value = undefined
+    }
+  }
+}
🧹 Nitpick comments (2)
packages/nuxt/src/app/nuxt.ts (1)

145-151: Internal _state map looks good; consider GC of stale keys

The addition is well‑typed and initialised correctly. To avoid long‑lived growth on the client, consider pruning entries (e.g. delete nuxtApp._state[key]) when a state is cleared without reset and no watchers remain. This keeps memory proportional to active state keys.

Also applies to: 319-319

packages/nuxt/src/app/composables/state.ts (1)

45-56: You can simplify to a plain ref; computed indirection is unnecessary

Given _state[key] and state are established above, returning state as Ref<T> is sufficient. The computed fallback to nuxtApp.payload.state[key] is dead code in this path and adds overhead.

Apply:

-  return computed({
-    get () {
-      return nuxtApp._state[key]?.data.value ?? nuxtApp.payload.state[key]
-    },
-    set (value) {
-      if (nuxtApp._state[key]) {
-        nuxtApp._state[key]!.data.value = value
-      } else {
-        nuxtApp.payload.state[key] = value
-      }
-    },
-  })
+  return state as Ref<T>

If you keep the computed, consider at least collapsing the branches to state.value for both get/set. As per coding guidelines.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 568cd34 and e7b2a3b.

📒 Files selected for processing (3)
  • packages/nuxt/src/app/composables/state.ts (2 hunks)
  • packages/nuxt/src/app/nuxt.ts (2 hunks)
  • test/nuxt/composables.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/nuxt.ts
  • test/nuxt/composables.test.ts
  • packages/nuxt/src/app/composables/state.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • test/nuxt/composables.test.ts
🧬 Code graph analysis (2)
test/nuxt/composables.test.ts (2)
packages/nuxt/src/app/composables/state.ts (2)
  • clearNuxtState (60-77)
  • useState (20-57)
packages/nuxt/src/app/nuxt.ts (1)
  • useNuxtApp (557-570)
packages/nuxt/src/app/composables/state.ts (1)
packages/nuxt/src/app/nuxt.ts (2)
  • useNuxtApp (557-570)
  • NuxtApp (206-206)
🔇 Additional comments (1)
test/nuxt/composables.test.ts (1)

203-207: Test coverage aligns with intended semantics

The scenarios comprehensively cover payload registration, unwrapped defaults, shared-key behaviour, and reset/remove paths. These rely on useState unref’ing the init return on initialisation; see my comment in state.ts proposing that fix.

Also applies to: 212-216, 217-224, 225-234, 243-285, 286-344

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Oct 20, 2025

Merging this PR will improve performance by 11.64%

⚡ 1 improved benchmark
✅ 19 untouched benchmarks
⏩ 3 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
initial dev server build in the basic test fixture 704 ms 630.6 ms +11.64%

Comparing Harm-Nullix:reset-to-init-useState (89c77ce) with main (c965e72)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Oct 20, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@33527

@nuxt/nitro-server

npm i https://pkg.pr.new/@nuxt/nitro-server@33527

nuxt

npm i https://pkg.pr.new/nuxt@33527

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@33527

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@33527

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@33527

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@33527

commit: 89c77ce

@Harm-Nullix
Copy link
Copy Markdown
Contributor Author

Quick example addition:

<script setup lang="ts">
import { useState } from '#app'

const stateWithoutKeyAndInit = useState<number | undefined>()
const stateWithKeyWithoutInit = useState<number | undefined>('WithKeyWithoutInit')
const stateWithoutKeyWithInit = useState<any>(() => ({
  spamm: 'eggs',
  green: 'ham',
  value: 1,
}))
const stateWithKeyWithInit = useState<any>('WithKeyWithInit', () => ({
  spamm: 'eggs',
  green: 'ham',
  value: 1,
}))

const resetState = (keys?: string | string[]) => {
  clearNuxtState(keys)
}
const reload = () => window.location.reload()

const state1 = useState('key', () => ref({
  test: 1,
}))
const state2 = useState('key', () => ref({
  test: 2,
}))
console.debug('state1.value :: ', state1.value.test)
console.debug('state2.value :: ', state2.value.test)
state1.value.test = 3
</script>

<template>
  <!-- Edit this file to play around with Nuxt but never commit changes! -->
  <div>
    Nuxt Playground

    <button @click="resetState()">
      Reset state
    </button>
    <button @click="reloadNuxtApp()">
      reload nuxt app
    </button>
    <button @click="reload()">
      reload window
    </button>
  </div>
  <div>
    <ul>
      <li>
        stateWithoutKeyAndInit :: {{ stateWithoutKeyAndInit }}
        <button @click="stateWithoutKeyAndInit = stateWithoutKeyAndInit ? stateWithoutKeyAndInit + 1 :1 ">
          set state
        </button>
      </li>
      <li>
        stateWithKeyWithoutInit :: {{ stateWithKeyWithoutInit }}
        <button @click="stateWithKeyWithoutInit = stateWithKeyWithoutInit ? stateWithKeyWithoutInit + 1 :1 ">
          set state
        </button>
      </li>
      <li>
        stateWithoutKeyWithInit :: {{ stateWithoutKeyWithInit }} <button @click="stateWithoutKeyWithInit.value += 1">
          set state
        </button>
      </li>
      <li>
        stateWithKeyWithInit :: {{ stateWithKeyWithInit }} <button @click="stateWithKeyWithInit.value += 1">
          set state
        </button>
      </li>
    </ul>

    {{ state1 }}. -- <br><br><br>
    {{ state2 }}
  </div>
</template>

<style scoped>

</style>

Copy link
Copy Markdown
Member

@huang-julien huang-julien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it can be really confusing for users and quite hard to debug if there's multiple useState that retuns differents default values

@Harm-Nullix
Copy link
Copy Markdown
Contributor Author

I think that it can be really confusing for users and quite hard to debug if there's multiple useState that retuns differents default values

I agree,
That is how states currently work right?
If you use a key twice, the second useState will just return the current payload of the first useState.

E.g. asyncData has the same logic: When you use asyncData with a default value, and the same key is used based on the url, you will get a other default value (or the current transformed value) from the other component that makes the same call, and not your own default.

You can tackle this by creating a composable around your useState as recommended that would be in charge of this specific key.

I think it is a valid point about data storage and the keys that are being used, I've come across this myself while useing UseFetch with autokeys, but I also think that topic is too brought to pick up in such a change as this, wouldn't you agree?

@Harm-Nullix
Copy link
Copy Markdown
Contributor Author

So what is next?
I am a bit lost in what to do with this now.
I feel like I made a clear case to at least discus.

  • I am not certain of how the PR flow goes (beyond the defaults)

@huang-julien
Copy link
Copy Markdown
Member

Yes that's just how it currently works with useAsyncData.
I think we can move on with this PR. I'll trigger a discussion with the team but this is probably the best solution.

const useStateKeyPrefix = '$s'
const getDefault = () => undefined

type InitOption<T> = (() => T | Ref<T>)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use MaybeRefOrGetter instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one, added


return computed({
get () {
return nuxtApp._state[key]?.data.value ?? nuxtApp.payload.state[key]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably don't want to have two possible reference for a state 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I've updated that to the state ref

if (key in nuxtApp.payload.state) {
nuxtApp.payload.state[key] = undefined
}
clearNuxtStateByKey(nuxtApp, useStateKeyPrefix + _key, reset ?? true)
Copy link
Copy Markdown
Member

@huang-julien huang-julien Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid breaking changes. We can make it true by default for v5 though by using a future flag

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, toggled the flag to be false bd

Copy link
Copy Markdown
Contributor Author

@Harm-Nullix Harm-Nullix Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huang-julien , is this something I can add to project as a future change, lets say as experimental?
It feels "intrusive" to do so in the Nuxt project.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
packages/nuxt/src/app/composables/state.ts (5)

9-10: Simplify type alias or rename to avoid confusion.

UseStateOptions<T> suggests an options object with multiple properties, but it's merely an alias for InitOption<T> (a function type). This naming can mislead users and maintainers.

Consider either:

  • Using InitOption<T> directly throughout, or
  • Renaming to UseStateInit<T> to reflect that it's a single init function rather than a structured options object.

Apply this diff to use InitOption<T> directly:

-type InitOption<T> = (() => MaybeRefOrGetter<T>)
-type UseStateOptions<T> = InitOption<T>
+type InitOption<T> = () => MaybeRefOrGetter<T>

Then replace UseStateOptions<T> with InitOption<T> on lines 18, 19, and 25.


21-24: Clarify the autoKey handling logic.

The autoKey extraction and re‑insertion logic is difficult to follow. The code pops the last argument if it's a string, then conditionally unshifts it back if args[0] is not a string. This approach is fragile and not immediately clear to maintainers.

Consider refactoring for clarity, or at minimum add inline comments explaining the intended argument patterns (e.g., (key, init), (init), (key), etc.) and how this logic resolves them.


45-45: Type assertion bypasses overload return‑type differences.

The as Ref<T> assertion reconciles two overloads that declare different return types (Ref<UnwrapRef<T>> vs Ref<T>). Whilst the runtime behaviour is correct (values are unwrapped via toValue), this assertion may obscure type‑checking issues and could lead to subtle inference problems for callers.

If the inconsistency (flagged earlier) is intentional, consider documenting why the types differ; otherwise, unify the return types to eliminate the need for this assertion.


69-77: Potential redundancy and intermediate state flicker when resetting.

When reset is true and nuxtApp._state[key] exists, the code performs two assignments:

  1. Line 71 sets nuxtApp.payload.state[key] = undefined (directly on the object)
  2. Line 75 sets nuxtApp._state[key]!.data.value = toValue(...) (through the ref)

Since _state[key].data is a toRef to payload.state[key] (created on line 36/38), they reference the same underlying value. The assignment on line 71 may cause an intermediate undefined state before line 75 overwrites it with the default, potentially triggering watchers or causing a visual flicker.

Consider skipping the line 71 assignment when _state[key] exists and reset is true, or reordering to set the reset value first.

Apply this diff to avoid the intermediate state:

 function clearNuxtStateByKey (nuxtApp: NuxtApp, key: string, reset: boolean): void {
-  if (key in nuxtApp.payload.state) {
-    nuxtApp.payload.state[key] = undefined
-  }
-
   if (nuxtApp._state[key]) {
     nuxtApp._state[key]!.data.value = reset ? toValue(nuxtApp._state[key]!._default) : undefined
+  } else if (key in nuxtApp.payload.state) {
+    // Clear legacy state entries that lack _state registry
+    nuxtApp.payload.state[key] = undefined
   }
 }

41-43: Consider error handling for init function execution.

The code calls toValue(defaultFn) (line 42) without catching potential errors. If the init function throws during initial hydration or reset, the exception will propagate and could break the component or app.

Consider wrapping the toValue call (here and on line 75) in a try‑catch to provide a more helpful error message or graceful fallback.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78ef915 and 20af136.

📒 Files selected for processing (1)
  • packages/nuxt/src/app/composables/state.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/composables/state.ts
🧬 Code graph analysis (1)
packages/nuxt/src/app/composables/state.ts (1)
packages/nuxt/src/app/nuxt.ts (2)
  • useNuxtApp (557-570)
  • NuxtApp (206-206)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: code
  • GitHub Check: build
🔇 Additional comments (4)
packages/nuxt/src/app/composables/state.ts (4)

1-3: LGTM! Imports support new functionality.

The added imports (MaybeRefOrGetter, UnwrapRef, toValue, and NuxtApp type) are all used appropriately throughout the refactored code.


18-19: Verify the return‑type inconsistency between overloads.

The first overload returns Ref<UnwrapRef<T>> whilst the second returns Ref<T>. Since both code paths use toValue(defaultFn) (line 42) to unwrap MaybeRefOrGetter<T>, both should exhibit the same unwrapping behaviour at runtime. This type mismatch may cause TypeScript inference issues when the key is omitted or auto-generated.

Confirm whether both overloads should return Ref<UnwrapRef<T>> for consistency, or clarify why they differ.


49-67: LGTM! clearNuxtState correctly delegates to per‑key helper.

The addition of the reset parameter and delegation to clearNuxtStateByKey maintains clarity. Defaulting reset to false preserves existing behaviour for callers who don't opt in to the new reset feature.


37-40: No action required—_state is properly defined and initialised.

Verification confirms that _state is correctly defined in the _NuxtApp interface (line 146–150, packages/nuxt/src/app/nuxt.ts) with the expected structure: Record<string, { data: Ref<unknown>, _default: () => MaybeRefOrGetter<unknown> } | undefined>. It is initialised in createNuxtApp (line 319) as shallowReactive({}), and the code at lines 37–40 correctly assigns the expected structure.

@Harm-Nullix Harm-Nullix force-pushed the reset-to-init-useState branch from 4cf927d to 8815072 Compare November 18, 2025 12:07
@Harm-Nullix
Copy link
Copy Markdown
Contributor Author

@huang-julien , I think I have done everything needed to make this:
A: compatible with what is working right now.
B: made it able to set a default value/getter that is used if wanted.

Please let me know if anything is needed on my side to move things along.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
docs/4.api/3.utils/clear-nuxt-state.md (1)

12-14: Improve sentence structure for clarity.

The phrase "initial value passing a second parameter" is grammatically awkward. Consider adding a comma or rephrasing for better readability.

Apply this diff to improve the grammar:

-You can also reset the state to the initial value passing a second parameter
+You can also reset the state to the initial value by passing a second parameter
packages/nuxt/src/app/composables/state.ts (2)

25-25: Consider stronger type safety for the destructuring.

The type assertion as [unknown, InitOption<T>] bypasses TypeScript's type checking. Given the complex autoKey logic above, this could mask runtime errors if args doesn't have the expected shape.

Consider either:

  1. Simplifying the argument handling logic to make the types provable, or
  2. Adding runtime validation to ensure args matches the expected structure before destructuring.

82-89: Consider using Nuxt's logger instead of console.error.

The console.error on line 86 logs errors directly to the console. For consistency with Nuxt's logging patterns and better control in production environments, consider using a Nuxt logger utility if available.

This is a minor suggestion since the error case (init function throwing) should be rare.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20af136 and 15c46a5.

📒 Files selected for processing (3)
  • docs/4.api/3.utils/clear-nuxt-state.md (1 hunks)
  • packages/nuxt/src/app/composables/state.ts (2 hunks)
  • packages/nuxt/src/app/nuxt.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/nuxt/src/app/nuxt.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/nuxt/src/app/composables/state.ts (1)
packages/nuxt/src/app/nuxt.ts (2)
  • useNuxtApp (559-572)
  • NuxtApp (208-208)
🪛 LanguageTool
docs/4.api/3.utils/clear-nuxt-state.md

[uncategorized] ~13-~13: Possible missing comma found.
Context: ...can also reset the state to the initial value passing a second parameter : ## Type ...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~24-~24: Loose punctuation mark.
Context: ...lean): void ``` ## Parameters - keys: One or an array of keys that are used i...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~25-~25: Loose punctuation mark.
Context: ...l state** will be invalidated. - reset: Reset the state to the initial value pa...

(UNLIKELY_OPENING_PUNCTUATION)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: code
  • GitHub Check: docs
🔇 Additional comments (5)
packages/nuxt/src/app/composables/state.ts (5)

1-10: LGTM! Clean type definitions and imports.

The import updates align with Vue 3 best practices, and the new type definitions (InitOption<T>, UseStateReturn<T>) clearly express the API surface. The getDefault helper is straightforward.


33-43: LGTM! State initialisation logic is solid.

The internal _state tracking and deferred initialisation using toValueWithFallback correctly handle both the initial setup and future reset scenarios. The previous concern about nested refs has been addressed.


49-67: LGTM! Clear and backward-compatible API extension.

The reset parameter is properly defaulted to false, maintaining backward compatibility whilst enabling the new reset-to-init behaviour. The delegation to clearNuxtStateByKey keeps the logic clean.


69-76: LGTM! Proper handling of both tracked and legacy state.

The function correctly handles both _state-tracked entries and legacy entries without registry, ensuring smooth backward compatibility and migration paths.


21-24: Remove the unused autoKey logic—it contradicts the type signatures and is never executed.

Based on verification across the codebase, the autoKey extraction (lines 21–24) is dead code. The type signatures guarantee that the last argument is either an InitOption<T> (a function) or undefined, never a string. All 30+ real-world calls follow the documented overload patterns (useState('key', () => init) or useState(() => init)), and none pass a string as the final parameter.

Delete lines 21–24 and update the implementation to directly destructure args:

export function useState<T> (...args: any): UseStateReturn<T> {
  const [_key, init] = args.length === 1 && typeof args[0] !== 'string' 
    ? [undefined, args[0]] 
    : args as [unknown, InitOption<T>]

Then throw the existing error if _key is missing, which will handle the useState(init) case properly.

@danielroe danielroe added this to the 4.3 milestone Dec 16, 2025
@Harm-Nullix
Copy link
Copy Markdown
Contributor Author

If: anything is needed from my end, please update me.
Else: I will simply wait

@danielroe
Copy link
Copy Markdown
Member

@Harm-Nullix I've made a few changes, including making reset default to true for compatibilityVersion: 5.

would love your thoughts (have I missed anything? any issues?) before I merge. 🙏

@danielroe danielroe added this pull request to the merge queue Feb 25, 2026
Merged via the queue into nuxt:main with commit 0a5a5c1 Feb 25, 2026
57 of 58 checks passed
@github-actions github-actions bot mentioned this pull request Feb 25, 2026
This was referenced Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clearNuxtState should take default value instead of undefined

3 participants