Skip to content
This repository has been archived by the owner on Aug 16, 2024. It is now read-only.
/ vue-ssr Public archive

Minimalistic wrapper to develop and run SSR Vue apps 🔗

License

Notifications You must be signed in to change notification settings

bistroo/vue-ssr

Repository files navigation

SSR for Vue

Minimalistic wrapper to run SSR Vue apps, based on Vite

Features

  • HMR support
  • Vue Router
  • State management
  • Teleports
  • Document head management (powered by @vueuse/head)

Quick Setup

Installation

pnpm install @bistroo/vue-ssr -D

Add the following scripts

"scripts": {
  "dev": "vue-ssr",
  "build": "vue-ssr build",
  "start": "vue-ssr start"
},

The vue-ssr command creates a dev server with HMR enabled. To create a production ready build, use vue-ssr build. After creating a build, use vue-ssr start to serve the build with Express.

Configuration

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 vite property with caution.

Usage

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.

License

MIT