Vite Plugin for Vue Server-Side State Synchronization
vite-plugin-vue-server-ref is a Vite plugin designed to facilitate the sharing of reactive state between multiple Vue clients and the Vite development server. It enables real-time synchronization of data across browser tabs or different client instances, utilizing Vue's reactivity system. The current stable version is v1.0.0, which notably transitioned to an ESM-only architecture, dropping CommonJS support. The package sees active development with regular updates addressing bug fixes and introducing minor features. Key differentiators include its tight integration with Vite's dev server, the use of virtual module imports (e.g., `server-ref:key`, `server-reactive:key`) for seamless state access, and features like granular synchronization control and incremental updates for reactive objects via diffing.
Common errors
-
ERR_REQUIRE_ESM
cause Attempting to `require()` `vite-plugin-vue-server-ref` in a CommonJS environment after it became ESM-only.fixChange `const ServerRef = require('vite-plugin-vue-server-ref')` to `import ServerRef from 'vite-plugin-vue-server-ref'` and ensure your Node.js or Vite configuration supports ESM. -
Property 'value' does not exist on type 'ServerRef<any>'
cause TypeScript's default type inference for virtual modules is `any`, leading to type errors when accessing properties like `.value` or specific keys on a reactive object.fixImport the correct types and apply a type assertion: `import type { ServerRef } from 'vite-plugin-vue-server-ref/client'; const foo = _foo as ServerRef<string>;` -
Failed to resolve import "server-ref:foo" from "src/App.vue". Does the file exist?
cause The `vite-plugin-vue-server-ref` plugin is not correctly configured or loaded in `vite.config.ts`, or the Vite dev server is not running.fixVerify that `vite-plugin-vue-server-ref` is added to the `plugins` array in your `vite.config.ts`. Ensure your `vite.config.ts` is correctly named and located, and that Vite is running in development mode. -
TypeError: Cannot read properties of undefined (reading 'count')
cause Accessing properties on a `server-reactive` object before it has been properly initialized or before the server has provided its state, or the key does not exist.fixEnsure the `state` object in your `vite.config.ts` has the key you are trying to access (e.g., `object.count`). If it's an asynchronous component, ensure proper loading states or fallbacks while the ref is being populated.
Warnings
- breaking Starting from v1.0.0, vite-plugin-vue-server-ref is exclusively an ESM (ECMAScript Module) package, dropping support for CommonJS. This requires using `import` statements in your `vite.config.ts` and any Node.js files that interact with the plugin.
- breaking Version 0.4.0 also introduced a breaking change by moving to `type: module` in its `package.json`, which affected CommonJS compatibility prior to the full ESM-only transition in v1.0.0.
- gotcha Virtual module imports like `server-ref:key` and `server-reactive:key` return an `any` type by default in TypeScript. This leads to a lack of type safety when accessing their properties (e.g., `.value` or object keys).
- gotcha When working with reactive objects, simply using `server-reactive:key` will send the entire object on every change. For incremental updates (deep diffing), you must append `?diff` to the virtual import path.
- gotcha The plugin exposes `$syncUp` and `$syncDown` properties on the server ref/reactive objects to control synchronization direction. Misunderstanding or misusing these can lead to unexpected behavior where state changes are not propagated.
Install
-
npm install vite-plugin-vue-server-ref -
yarn add vite-plugin-vue-server-ref -
pnpm add vite-plugin-vue-server-ref
Imports
- ServerRef
const ServerRef = require('vite-plugin-vue-server-ref')import ServerRef from 'vite-plugin-vue-server-ref'
- server-ref:key
import { foo } from 'server-ref:foo'import foo from 'server-ref:foo'
- server-reactive:key
import { object } from 'server-reactive:object'import object from 'server-reactive:object'
- ServerRef (type)
import { ServerRef, ServerReactive } from 'vite-plugin-vue-server-ref/client'import type { ServerRef, ServerReactive } from 'vite-plugin-vue-server-ref/client'
Quickstart
import { defineConfig } from 'vite';
import ServerRef from 'vite-plugin-vue-server-ref';
import Vue from '@vitejs/plugin-vue';
// vite.config.ts
export default defineConfig({
plugins: [
Vue(),
ServerRef({
state: {
foo: 'bar',
object: {
count: 0,
message: 'Hello'
}
}
})
]
});
// src/main.ts (or a Vue component)
import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');
// src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import type { ServerReactive, ServerRef } from 'vite-plugin-vue-server-ref/client';
const foo = (await import('server-ref:foo')).default as ServerRef<string>;
const object = (await import('server-reactive:object?diff')).default as ServerReactive<{ count: number; message: string }>;
console.log('Initial foo:', foo.value); // Should log 'bar'
console.log('Initial object:', object.count); // Should log 0
foo.value = 'updated string';
object.count++;
object.message = 'World';
// You can also listen for changes from the server/other clients
foo.$onSet((newValue) => {
console.log(`Foo changed from server/client: ${newValue}`);
});
// Example of controlling sync direction
// foo.$syncUp = false; // Makes it download-only
// object.$syncDown = false; // Makes it upload-only
const localCount = ref(0);
setInterval(() => {
localCount.value++;
// This won't sync to server unless foo.$syncUp is true and value is changed
// console.log('Local count:', localCount.value);
}, 1000);
</script>
<template>
<div>
<h1>Server Ref Demo</h1>
<p>Foo: {{ foo.value }}</p>
<p>Object Count: {{ object.count }}</p>
<p>Object Message: {{ object.message }}</p>
<p>Local Count: {{ localCount }}</p>
</div>
</template>