@@ -21,6 +21,8 @@ export interface CookieOptions<T = any> extends _CookieOptions {
2121default ?:( ) => T | Ref < T >
2222watch ?:boolean | 'shallow'
2323readonly ?:boolean
24+ // refresh cookie expiration even when the value remains unchanged after writing
25+ refresh ?:boolean
2426}
2527
2628// eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -31,6 +33,7 @@ const CookieDefaults = {
3133watch :true ,
3234decode :val => destr ( decodeURIComponent ( val ) ) ,
3335encode :val => encodeURIComponent ( typeof val === 'string' ?val :JSON . stringify ( val ) ) ,
36+ refresh :false ,
3437} satisfies CookieOptions < any >
3538
3639// we use globalThis to avoid crashes in web workers
@@ -57,10 +60,10 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
5760const shouldSetInitialClientCookie = import . meta. client && ( hasExpired || cookies [ name ] === undefined || cookies [ name ] === null )
5861const cookieValue = klona ( hasExpired ?undefined :( cookies [ name ] as any ) ?? opts . default ?.( ) )
5962
60- // use a custom ref to expire the cookie on client side otherwise usebasic ref
63+ // use a custom ref to expire the cookie on client side otherwise usecookieServerRef
6164const cookie = import . meta. client && delay && ! hasExpired
6265 ?cookieRef < T | undefined > ( cookieValue , delay , opts . watch && opts . watch !== 'shallow' )
63- :ref < T | undefined > ( cookieValue )
66+ :cookieServerRef < T | undefined > ( name , cookieValue )
6467
6568if ( import . meta. dev && hasExpired ) {
6669console . warn ( `[nuxt] not setting cookie \`${ name } \` as it has already expired.` )
@@ -131,7 +134,7 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
131134if ( opts . watch ) {
132135watch ( cookie , ( ) => {
133136if ( watchPaused ) { return }
134- callback ( )
137+ callback ( opts . refresh )
135138} ,
136139{ deep :opts . watch !== 'shallow' } )
137140}
@@ -142,7 +145,16 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
142145} else if ( import . meta. server ) {
143146const nuxtApp = useNuxtApp ( )
144147const writeFinalCookieValue = ( ) => {
145- if ( opts . readonly || isEqual ( cookie . value , cookies [ name ] ) ) { return }
148+ if (
149+ opts . readonly
150+ || ( isEqual ( cookie . value , cookies [ name ] ) && ! opts . refresh )
151+ ) { return }
152+
153+ nuxtApp . _cookiesChanged ||= { }
154+ if ( opts . refresh && ! nuxtApp . _cookiesChanged [ name ] ) {
155+ return
156+ }
157+
146158nuxtApp . _cookies ||= { }
147159if ( name in nuxtApp . _cookies ) {
148160// do not append a second `set-cookie` header
@@ -259,3 +271,25 @@ function cookieRef<T> (value: T | undefined, delay: number, shouldWatch: boolean
259271}
260272} )
261273}
274+
275+ // custom ref that will track explicit cookie writes on the server (used for `refresh` option)
276+ function cookieServerRef < T > ( name :string , value :T | undefined ) {
277+ const internalRef = ref ( value )
278+
279+ return customRef ( ( track , trigger ) => {
280+ return {
281+ get ( ) {
282+ track ( )
283+ return internalRef . value
284+ } ,
285+ set ( newValue ) {
286+ const nuxtApp = useNuxtApp ( )
287+ nuxtApp . _cookiesChanged ||= { }
288+ nuxtApp . _cookiesChanged [ name ] = true
289+
290+ internalRef . value = newValue
291+ trigger ( )
292+ } ,
293+ }
294+ } )
295+ }