Minimalistic wrapper to run SSR Vue apps, based on Vite
- HMR support
- Vue Router
- State management
- Teleports
- Document head management (powered by @vueuse/head)
pnpm install @bistroo/vue-ssr -DAdd the following scripts
"scripts": {
  "dev": "vue-ssr",
  "build": "vue-ssr build",
  "start": "vue-ssr start"
},The
vue-ssrcommand creates a dev server with HMR enabled. To create a production ready build, usevue-ssr build. After creating a build, usevue-ssr startto serve the build with Express.
Create a vue-ssr.config.ts
import { defineConfig } from '@bistroo/vue-ssr'
import { fileURLToPath } from 'node:url'
export default defineConfig({
  vite: {
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url)),
      },
    },
  },
})Use the
viteproperty with caution.
import { vueSSR } from '@bistroo/vue-ssr'
import App from '@/App.vue'
const Counter = () => import('@/Counter.vue')
const routes = [
  {
    path: '/',
    name: 'counter',
    component: Counter,
  }
]
export default vueSSR(App, { routes })The main.ts file should export the imported vueSSR function.
Pinia is supported by using the app and state property inside the callback.
export default vueSSR(App, { routes }, ({ app, state }) => {
  const pinia = createPinia()
  app.use(pinia)
  if (import.meta.env.SSR) {
    state.value = pinia.state.value
  } else {
    pinia.state.value = state.value
  }
})The state will be persisted on
window.__INITIAL_STATE__property and serialized using@nuxt/devalue
It's possible to make changes to the router, use the router property in the callback.
export default vueSSR(App, { routes }, ({ router }) => {
  router.beforeEach(async (to, from) => {
    if (
      !isAuthenticated &&
      to.name !== 'Login'
    ) {
      return { name: 'Login' }
    }
  })
})The Express request and response objects are accessible from the callback. Make sure to wrap them in import.meta.env.SSR.
export default vueSSR(App, { routes }, ({ request, response }) => {
  if (import.meta.env.SSR) {
    console.log(request?.originalUrl)
  }
})Or use useSSRContext.
const { request, response } = useSSRContext()
if (import.meta.env.SSR) {
  console.log(request?.originalUrl)
}Using Teleport is supported, but requires a little bit of setup. Targeting body is not supported, use #teleports instead.
<template>
  <Teleport to="#teleports">
    <button @click="store.increment">Increment</button>
  </Teleport>
</template>During SSR, the Teleport component will be rendered as a div with the id set to the to property.
MIT