npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nuxt-async-cache

v0.0.14

Published

Caching system for Nuxt with IndexedDB, automatic refetching, and cross-tab synchronization

Readme

Nuxt Async Cache

Caching system for Nuxt with IndexedDB, automatic refetching, and cross-tab synchronization.

  • SSR-enabled: works with Nuxt SSR
  • IndexedDB Storage: persistent client-side caching using Dexie
  • Auto-refresh: flexible data refreshing
  • Cross-tab Sync: coordinated data updates across multiple tabs
  • Lock Coordination: de-duplicated requests across tabs

Installation

1. Install the package

# Using pnpm (recommended)
pnpm add nuxt-async-cache

# Using npm
npm install nuxt-async-cache

# Using yarn
yarn add nuxt-async-cache

2. Add to Nuxt modules

Add the module to your nuxt.config.ts:

export default defineNuxtConfig({
  modules: [
    'nuxt-async-cache'
  ],
  
  // Optional: Configure module options
  asyncCache: {
    // Module configuration options (if any)
  }
})

Note: The module automatically configures Vite to handle the Dexie dependency correctly. If you encounter import issues, you can add this fallback configuration:

export default defineNuxtConfig({
  modules: ['nuxt-async-cache'],
  vite: {
    resolve: {
      alias: {
        'dexie': 'dexie/dist/dexie.mjs'
      }
    }
  }
})

3. Start using the composable

The module automatically provides the useCachedAsyncData composable across your entire Nuxt application:

<script setup>
// No imports needed - auto-imported by the module
const { data, pending } = useCachedAsyncData({
  key: 'my-data',
  handler: () => $fetch('/api/data')
})
</script>

Basic Usage

<script setup>
const { data, pending, error, refresh, clear } = useCachedAsyncData({
  key: 'user-profile',
  handler: () => $fetch('/api/user/profile'),
  defaults: {
    staleAfter: 60000,      // 1 minute
    refetchAfter: 300000,   // 5 minutes  
    expiresAfter: 3600000   // 1 hour
  }
})
</script>

<template>
  <div>
    <div v-if="pending">Loading...</div>
    <div v-else-if="error">Error: {{ error.message }}</div>
    <div v-else-if="data">
      <h1>{{ data.name }}</h1>
      <button @click="refresh()">Refresh</button>
      <button @click="clear()">Clear Cache</button>
    </div>
  </div>
</template>

Reactive Keys

The key parameter supports reactive values, allowing you to dynamically change the cache key:

<script setup>
const userId = ref('123')
const userType = ref('admin')

// Reactive key that updates when dependencies change
const { data, pending } = useCachedAsyncData({
  key: computed(() => `user-${userId.value}-${userType.value}`),
  handler: () => $fetch(`/api/users/${userId.value}?type=${userType.value}`)
})

// When userId or userType changes, the cache key changes
// and data is automatically refetched for the new key
function switchUser(newId, newType) {
  userId.value = newId
  userType.value = newType
  // Data will automatically update for new cache key
}
</script>

You can also use refs directly:

<script setup>
const cacheKey = ref('initial-key')

const { data } = useCachedAsyncData({
  key: cacheKey, // Reactive ref
  handler: () => $fetch('/api/data')
})

// Changing the key will switch to different cached data
cacheKey.value = 'different-key'
</script>

Advanced Usage with Dynamic Timestamps

const { data } = useCachedAsyncData({
  key: 'api-data',
  handler: () => $fetch('/api/data'),
  timestamps: {
    // Use server-provided timestamp
    requestTime: (response) => response.timestamp,
    
    // Compute staleness based on data type
    staleAfter: (response) => {
      if (response.type === 'critical') return 10000  // 10 seconds
      if (response.type === 'normal') return 60000    // 1 minute
      return 300000  // 5 minutes for others
    },
    
    // Auto-refresh based on server hint
    refetchAfter: (response) => response.nextUpdateIn || null,
    
    // Expiry based on data importance
    expiresAfter: (response) => response.ttl || 86400000
  }
})

API Reference

useCachedAsyncData(options)

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | key | MaybeRefOrGetter<string> | Required | Unique cache key (supports reactive values) | | handler | () => Promise<T> | Required | Data fetching function | | timestamps | TimestampConfig<T> | {} | Dynamic timestamp configuration | | defaults | DefaultTimestamps | See below | Default timing values | | immediate | boolean | true | Fetch immediately | | server | boolean | true | Fetch on server | | lazy | boolean | false | Don't block navigation | | dedupe | 'defer' \| 'cancel' | undefined | Deduplication strategy |

Default Timestamps

{
  staleAfter: 30000,     // 30 seconds
  refetchAfter: null,    // No auto-refetch
  expiresAfter: 86400000 // 24 hours
}

Return Value

{
  data: Ref<T | null>,           // Reactive data
  pending: Ref<boolean>,         // Loading state
  error: Ref<Error | null>,      // Error state
  status: Ref<Status>,           // 'idle' | 'pending' | 'success' | 'error'
  refresh: () => Promise<void>,  // Manual refresh
  clear: () => Promise<void>     // Clear cache entry
}

Cache Management Utilities

The module provides client-side utilities for cache management:

// In a Nuxt plugin or client composable
import { 
  cleanupExpiredCache,
  clearAllCache,
  setupCacheMaintenance
} from 'nuxt-async-cache/runtime/utils/cache'

// Manual cleanup (client-side only)
await cleanupExpiredCache()

// Clear everything
await clearAllCache()

// Auto maintenance (runs every 5 minutes by default, client-side only)
const stopMaintenance = setupCacheMaintenance()
// Call stopMaintenance() to stop

How It Works

1. SSR Phase

  • Data is fetched on the server and stored in memory
  • Cache entries are passed to the client via Nuxt payload

2. Client Hydration

  • Client checks IndexedDB for newer data
  • Uses server data if local cache is older or missing
  • Sets up auto-refresh timers based on timestamps

3. Cross-Tab Coordination

  • Uses Web Locks API for coordinating refresh across tabs
  • Falls back to Dexie-based locking if Web Locks unavailable
  • BroadcastChannel notifies other tabs of cache updates

4. Smart Refreshing

  • Stale: Data is shown but background refresh is triggered
  • Refetch: Automatic refresh at specified intervals
  • Expired: Data is removed from cache entirely

Best Practices

  1. Choose appropriate cache keys: Use descriptive, unique keys. Keys can be static strings, reactive refs, or computed values for dynamic caching
  2. Set reasonable timeouts: Balance freshness vs performance
  3. Handle errors gracefully: Always provide error states
  4. Use dynamic timestamps: Leverage server hints when available
  5. Monitor cache size: Use cleanup utilities in production
  6. Leverage reactive keys: Use computed keys for dynamic data that depends on user state or route parameters

Browser Support

  • IndexedDB: All modern browsers
  • Web Locks API: Chrome 69+, Firefox 96+, Safari 15.4+
  • BroadcastChannel: All modern browsers

Graceful fallbacks are provided for unsupported APIs.

Development

Setup

# Clone the repository
git clone <repository-url>
cd nuxt-async-cache

# Install dependencies
pnpm install

# Prepare the module
pnpm dev:prepare

# Start development server
pnpm dev

Testing

# Run tests
pnpm test

# Run tests in watch mode
pnpm test:watch

# Run type checking
pnpm test:types

Building

# Build the module
pnpm prepack

# Lint code
pnpm lint