Skip to content

Conversation

@riabinkovihor
Copy link

@riabinkovihor riabinkovihor commented Dec 3, 2025

🔗 Linked issue

resolves #33811

📚 Description

Add a refresh option to useCookie to extend the cookie's expiration even when its value is unchanged.

Example:

const session = useCookie('session-id', {
  maxAge: 60 * 60,
  refresh: true
})

// Will now extend expiration each time, even with same value
session.value = session.value

@bolt-new-by-stackblitz
Copy link

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 3, 2025

Open in StackBlitz

@nuxt/kit

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

@nuxt/nitro-server

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

nuxt

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

@nuxt/rspack-builder

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

@nuxt/schema

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

@nuxt/vite-builder

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

@nuxt/webpack-builder

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

commit: 585c8e1

@coderabbitai
Copy link

coderabbitai bot commented Dec 3, 2025

Walkthrough

Adds a refresh?: boolean option to CookieOptions<T> and exposes it in the cookie composable. Introduces server-side tracking via a cookieServerRef and adds _cookiesChanged?: Record<string, boolean> to the Nuxt app interface to record explicit server writes. Client-side watch callbacks are invoked with the refresh flag so refresh-triggered writes can occur even if the value is unchanged. Server-side logic now skips redundant writes when readonly or when value is unchanged unless refresh is requested, and coordinates deduplicated or deferred header emission for SSR responses.

Possibly related PRs

  • nuxt/nuxt PR 31517: Modifies the same useCookie/cookie composable logic to accept a force/refresh-like flag and adjusts client- and server-side cookie write/refresh behaviour, touching cookie write and tracking code.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main feature addition: a new refresh option for the useCookie composable, which directly matches the primary change in the changeset.
Description check ✅ Passed The description accurately explains the feature by providing context, rationale, and a practical code example showing how the refresh option enables cookie expiration extension with unchanged values.
Linked Issues check ✅ Passed The implementation fully addresses the linked issue #33811 by introducing a refresh boolean option to useCookie that extends cookie expiration on explicit writes regardless of value changes, with proper server and client-side integration.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the refresh feature: cookie composable logic, Nuxt app state tracking, and documentation updates directly support the stated objective without extraneous modifications.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

✨ Issue Enrichment is now available for GitHub issues!

CodeRabbit can now help you manage issues more effectively:

  • Duplicate Detection — Identify similar or duplicate issues
  • Related Issues & PRs — Find relevant issues and PR's from your repository
  • Suggested Assignees — Find the best person to work on the issue
  • Implementation Planning — Generate detailed coding plans for engineers and agents
Disable automatic issue enrichment

To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

issue_enrichment:
  auto_enrich:
    enabled: false

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

@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: 2

🧹 Nitpick comments (2)
packages/nuxt/src/app/composables/cookie.ts (2)

82-90: refresh currently bypasses the readonly guard in the client callback

On the client, callback and the watcher now behave as:

const callback = (force = false) => {
  if (!force) {
    if (opts.readonly || isEqual(cookie.value, cookies[name])) { return }
  }
  writeClientCookie(...)
  
}

if (opts.watch) {
  watch(cookie, () => {
    if (watchPaused) { return }
    callback(opts.refresh)
  }, { deep: opts.watch !== 'shallow' })
}

When refresh: true, the watcher always calls callback(true), which skips the opts.readonly branch and allows writes even for cookies declared as readonly. While readonly is mostly a TypeScript‑level contract, it is still surprising that enabling refresh changes the runtime behaviour from “never write” to “may write”.

If you want readonly to remain a hard guarantee, you could separate the two concerns:

-    const callback = (force = false) => {
-      if (!force) {
-        if (opts.readonly || isEqual(cookie.value, cookies[name])) { return }
-      }
+    const callback = (force = false) => {
+      if (opts.readonly) { return }
+      if (!force && isEqual(cookie.value, cookies[name])) { return }
       writeClientCookie(name, cookie.value, opts as CookieSerializeOptions)
       …
     }

    if (opts.watch) {
      watch(cookie, () => {
        if (watchPaused) { return }
-        callback(opts.refresh)
+        callback(opts.refresh)
      }, { deep: opts.watch !== 'shallow' })
    }

This preserves the “force refresh even when the value is unchanged” behaviour while ensuring readonly cookies are never written from the client.

Also applies to: 134-140


92-99: BroadcastChannel refresh handling is coherent, but consider the source tab’s behaviour

The new handleChange signature:

const handleChange = (data: { value?: any, refresh?: boolean }) => {
  const value = data.refresh ? readRawCookies(opts)?.[name] : opts.decode(data.value)
  
}

allows tabs that receive { refresh: true } (via refreshCookie) to reread the cookie from document.cookie, which is sensible: they do not rely on another tab’s encoded payload and instead use their own view of the cookie jar.

One nuance worth double‑checking is that refreshCookie(name) on the current tab only posts a message; it does not itself call the local callback that writes the cookie again with a fresh maxAge. That means:

  • The tab that calls refreshCookie does not extend the cookie’s expiry by itself.
  • Only tabs that have a useCookie watcher and receive the broadcast will update their local ref to whatever is already in document.cookie.

If the intent is that refreshCookie should actively extend the cookie TTL in the caller as well, you may want to either call the same write path locally, or document that refreshCookie is purely a cross‑tab synchronisation helper and relies on some other code path to perform the actual refresh.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a62708 and f3df555.

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

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

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/composables/cookie.ts
  • packages/nuxt/src/app/nuxt.ts
**/*.{ts,tsx,js,jsx,vue}

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

**/*.{ts,tsx,js,jsx,vue}: Use clear, descriptive variable and function names
Add comments only to explain complex logic or non-obvious implementations
Keep functions focused and manageable (generally under 50 lines), and extract complex logic into separate domain-specific files
Remove code that is not used or needed
Use error handling patterns consistently

Files:

  • packages/nuxt/src/app/composables/cookie.ts
  • packages/nuxt/src/app/nuxt.ts
🧬 Code graph analysis (1)
packages/nuxt/src/app/composables/cookie.ts (1)
packages/nuxt/src/app/nuxt.ts (1)
  • useNuxtApp (552-565)
⏰ 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: test-fixtures (ubuntu-latest, built, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: code
🔇 Additional comments (2)
packages/nuxt/src/app/composables/cookie.ts (2)

18-26: refresh option and default integrate cleanly into existing API

The addition of refresh?: boolean to CookieOptions and the corresponding refresh: false default in CookieDefaults look consistent and backwards compatible. The comment clearly documents the intent and the option is opt‑in, so existing callers are unaffected.

No changes requested here.

Also applies to: 31-37


231-273: cookieRef expiry refactor remains sound with refresh changes

The custom cookieRef that tracks cookie expiry client‑side still correctly:

  • Resets its internal timeout on every set.
  • Handles large delays by chunking into MAX_TIMEOUT_DELAY.
  • Clears timeouts and watchers on scope dispose.

The new refresh option uses the existing watcher/callback mechanism, so this ref continues to behave as before and does not appear to introduce new edge cases.

No changes requested here.

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 3, 2025

CodSpeed Performance Report

Merging #33814 will improve performances by 16.63%

Comparing riabinkovihor:feat/cookie-refresh-option (585c8e1) with main (2a91221)

Summary

⚡ 1 improvement
✅ 9 untouched

Benchmarks breakdown

Benchmark BASE HEAD Change
writeTypes in the basic-types fixture 94.1 ms 80.7 ms +16.63%

return internalRef.value
},
set (newValue) {
const nuxtApp = useNuxtApp()
Copy link
Member

Choose a reason for hiding this comment

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

I think we should call usenxtApp at line 294.

Copy link
Author

Choose a reason for hiding this comment

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

Agreed, I’ve moved useNuxtApp.

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77d0a54 and 1685e51.

📒 Files selected for processing (1)
  • docs/4.api/2.composables/use-cookie.md (3 hunks)
🧰 Additional context used
🪛 GitHub Actions: autofix.ci
docs/4.api/2.composables/use-cookie.md

[error] 64-64: markdownlint MD060: Table column style [Table pipe does not align with heading for style "aligned"]. Command failed: markdownlint ./docs --fix && case-police 'docs/**/*.md' *.md --fix

⏰ 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). (20)
  • GitHub Check: test-fixtures (windows-latest, built, rspack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite-env-api, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite-env-api, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite-env-api, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite-env-api, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite-env-api, default, manifest-on, json, lts/-1)
  • GitHub Check: release-pkg-pr-new
  • GitHub Check: test-size
  • GitHub Check: typecheck (ubuntu-latest, bundler)
  • GitHub Check: typecheck (windows-latest, bundler)
  • GitHub Check: code
🔇 Additional comments (1)
docs/4.api/2.composables/use-cookie.md (1)

168-188: Example section is well-structured and demonstrates the feature effectively.

The "Refreshing Cookies" example clearly shows the refresh option in action, with helpful comments explaining that the expiration updates even when the value remains unchanged. This provides good practical guidance for users implementing session-like cookie behaviour.

Copy link

@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

♻️ Duplicate comments (1)
docs/4.api/2.composables/use-cookie.md (1)

64-64: Markdown table alignment issue (duplicate of previous review).

The MD060 markdownlint error regarding pipe alignment on this line was previously flagged. Ensure the table pipes are consistently aligned with the heading row. Run markdownlint ./docs --fix to auto-correct the alignment.

🧹 Nitpick comments (1)
docs/4.api/2.composables/use-cookie.md (1)

168-188: Example could better demonstrate the intended use case.

The example shows a single assignment in <script setup>, which doesn't fully illustrate the intended use case of refreshing cookie expiration on repeated activity. Consider enhancing the example to show multiple assignments (e.g., triggered by user interactions) to clarify when and why refresh: true is beneficial.

For instance, the example could demonstrate assigning the same value in response to user activity (like button clicks) to extend the session expiration, which is the primary motivation for this feature.

Would you like me to suggest an enhanced version of this example that better demonstrates the activity-based refresh pattern?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1685e51 and 585c8e1.

📒 Files selected for processing (1)
  • docs/4.api/2.composables/use-cookie.md (3 hunks)
⏰ 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). (3)
  • GitHub Check: codeql (javascript-typescript)
  • GitHub Check: build
  • GitHub Check: code
🔇 Additional comments (1)
docs/4.api/2.composables/use-cookie.md (1)

39-39: Interface property addition is correct.

The new refresh?: boolean option follows the established pattern and is properly integrated into the CookieOptions<T> interface.

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.

Add refresh option to useCookie() to extend cookie expiration

2 participants