Stay Updated!

Get the latest posts and insights delivered directly to your inbox

Skip to content
Alexander Opalic
Alexander Opalic

TIL - Detecting fresh deployments in a Vue 3 SPA with vite + version.json

Learn how to detect fresh deployments in a Vue 3 SPA with vite + version.json

Today I discovered a very simple way for my Vue 3 single-page app to tell users when I’ve released a new update.

The idea#

  1. Build-time: Each time I run vite build, a small Vite plugin creates a version.json file. It also sets up some information that’s available when the app is built.
  2. Run-time: The app regularly checks the /version.json file.
  3. If the version or tag in that file doesn’t match the information the app was built with, we show a message like “New version available – refresh!”

Injects constants

Accesses

Vite Build Process

version-json Plugin

Environment Variables:
• import.meta.env.APP_VERSION
• import.meta.env.APP_TAG

Application Code

Compile-Time Access

The Vite plugin ( plugins/version-json.ts )#

export default function versionJson(): Plugin {
  // …snip…
  const versionDetails = {
    name: pkg.name,
    version: pkg.version, // 2.0.0
    tag: gitTag(), // v2.0.0-2-gabcdef
    commit: gitCommitHash(), // abcdef…
    commitTime: gitCommitTimestamp(),
    created: new Date().toISOString(),
  };

  return {
    name: "version-json",
    // 👉 1. Serve /version.json in dev
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        if (req.url === "/version.json") {
          res.setHeader("Content-Type", "application/json");
          res.end(JSON.stringify(versionDetails, null, 2));
          return;
        }
        next();
      });
    },

    // 👉 2. Emit dist/version.json in production
    closeBundle() {
      writeFileSync(
        resolve(outDir, "version.json"),
        JSON.stringify(
          { ...versionDetails, created: new Date().toISOString() },
          null,
          2
        )
      );
    },
  };
}

The plugin also makes import.meta.env.APP_VERSION and APP_TAG available, so the app always knows which version it’s running.


The watcher component ( ShowNewVersion.vue )#

<script setup lang="ts">
const props = defineProps<{
  initialVersion: string | null;
  initialTag: string | null;
}>();
const { isNewVersion, details } = useCheck();

function useCheck() {
  const isNewVersion = ref(false);
  const details = ref<string | null>(null);

  async function poll() {
    if (!props.initialVersion) return;
    const res = await fetch("/version.json");
    const v = await res.json();
    if (v.version !== props.initialVersion || v.tag !== props.initialTag) {
      isNewVersion.value = true;
      details.value = `${v.version} (${v.tag})`;
    }
  }
  onMounted(() => poll(), setInterval(poll, 30_000));
  return { isNewVersion, details };
}
</script>

<template>
  <div v-if="isNewVersion" class="alert">
    A new version is available → refresh! {{ details }}
  </div>
</template>

How I use it#

I run this command:

npm run build        # emits dist/version.json

When people use the app, their browser checks the /version.json file every 30 seconds. As soon as the file’s content changes, the yellow banner appears. They can then do a manual refresh (⌘R) to load the new version.

Takeaways#

  • No server changes needed – it’s just one static file.
  • It works the same way when I’m developing locally (the file is served on the fly) and in the live app (the file is created once during the build).
  • You get information at build-time and a check at run-time, all in one.
  • You can find the full code I showed above in this repository → vueNewVersion.

For more details, you can look at the Deep Wiki documentation.

That’s it – one plugin, one tiny component, and an easy way to manage cache-busting!

#vue #vite