The main composable for interacting with Nuxt Skew Protection.
const skew = useSkewProtection()
manifestRef<NuxtAppManifestMeta | null>The current build manifest from builds/latest.json.
const { manifest } = useSkewProtection()
console.log(manifest.value?.id) // Current buildId
console.log(manifest.value?.timestamp) // Build timestamp
clientVersionstringThe user's current version (from cookie or buildId).
const { clientVersion } = useSkewProtection()
console.log(clientVersion) // "abc123"
isOutdatedComputedRef<boolean>Whether the user's version is outdated.
const { isOutdated } = useSkewProtection()
if (isOutdated.value) {
console.log('User is on an old version')
}
checkForUpdates()() => Promise<void>Manually trigger an update check.
const { checkForUpdates } = useSkewProtection()
// Check for updates
await checkForUpdates()
onAppOutdated()(callback: (manifest: NuxtAppManifestMeta | null) => void) => voidTriggers when a new version is detected, regardless of whether it affects the current session. Uses Nuxt's built-in app:manifest:update hook.
Use case: Make next page load a hard navigation (forces HTML reload).
const { onAppOutdated } = useSkewProtection()
onAppOutdated((manifest) => {
console.log('New version available:', manifest?.id)
// Show passive "Update available" badge
})
onCurrentChunksOutdated()(callback: (payload: ChunksOutdatedPayload) => void) => voidTriggers only when the user's loaded JavaScript modules have been changed in a new deployment.
Consider the case of a user on a blog post when you deploy a new version that only changes the homepage. The user's session is unaffected
and they can continue browsing without interruption. In this case, onCurrentChunksOutdated will not trigger.
Under the hood this tracks modules using a service worker.
const { onCurrentChunksOutdated } = useSkewProtection()
onCurrentChunksOutdated((payload) => {
console.log('Deleted chunks:', payload.deletedChunks)
console.log('Invalidated modules:', payload.invalidatedModules)
console.log('Passed releases:', payload.passedReleases)
// Show notification
alert('Please reload to get the latest version')
})
interface NuxtAppManifestMeta {
id: string
timestamp: number
skewProtection?: {
current: string
versions: Record<string, VersionInfo>
}
}
interface VersionInfo {
timestamp: string
expires: string
assets: string[]
deletedChunks?: string[]
}
interface ChunksOutdatedPayload {
deletedChunks: string[]
invalidatedModules: string[]
passedReleases: string[]
}
const { onCurrentChunksOutdated } = useSkewProtection()
onCurrentChunksOutdated(({ invalidatedModules, passedReleases }) => {
// Track in analytics
analytics.track('chunks_outdated', {
count: invalidatedModules.length,
modules: invalidatedModules,
releases: passedReleases
})
// Show custom UI
showUpdateDialog({
title: 'Update Required',
message: `${invalidatedModules.length} modules need updating (${passedReleases.length} releases passed)`
})
})
<script setup>
const { clientVersion, manifest } = useSkewProtection()
const buildDate = computed(() => {
if (!manifest.value?.timestamp)
return null
return new Date(manifest.value.timestamp).toLocaleString()
})
</script>
<template>
<div class="version-info">
<p>Your version: {{ clientVersion }}</p>
<p>Latest version: {{ manifest?.id }}</p>
<p>Build date: {{ buildDate }}</p>
</div>
</template>
Note: Only relevant if you're using polling.
const { checkForUpdates, isOutdated } = useSkewProtection()
async function handleUserAction() {
// Check for updates before critical action
await checkForUpdates()
if (isOutdated.value) {
const shouldReload = confirm('A new version is available. Reload now?')
if (shouldReload) {
window.location.reload()
}
}
else {
// Proceed with action
await performCriticalAction()
}
}