Skip to content

yooouuri/vite-plugin-vue-ssr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

5a90ff5 · Dec 12, 2024

History

52 Commits
Dec 12, 2024
Aug 30, 2023
Jul 29, 2024
Dec 12, 2024
Dec 12, 2024
Aug 30, 2023
Dec 20, 2023
Dec 12, 2024
Jul 29, 2024
Dec 12, 2024
Dec 12, 2024
Dec 12, 2024
Dec 12, 2024
Apr 24, 2024
Dec 12, 2024
Jul 29, 2024

Repository files navigation

vite-plugin-vue-ssr

Vite plugin to develop Vue SSR apps

Features

  • Vue Router
  • State management
  • Teleports
  • Unhead support
  • Based on H3

Quick Setup

Installation

pnpm install vite-plugin-vue-ssr -D

Install devalue if you need to hydrate the state

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueSsr from 'vite-plugin-vue-ssr/plugin'
import { fileURLToPath } from 'node:url'

export default defineConfig({
  plugins: [
    vue(),
    vueSsr(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
})

Usage

Add the build commands to your package.json file.

{
  "scripts": {
    "dev": "vite",
    "build": "pnpm run build:client && pnpm run build:server",
    "build:client": "vite build --ssrManifest --outDir dist/client",
    "build:server": "vite build --ssr src/main.ts --outDir dist/server"
  }
}

This will build a client and server bundle.

Use the vite command to start a SSR enabled dev server.

Disabling SSR in vite will enable to build a SPA version.

The main.ts file should export the imported vueSSR function.

import { vueSSR } from 'vite-plugin-vue-ssr'

import App from '@/App.vue'

const Counter = () => import('@/Counter.vue')

const routes = [
  {
    path: '/',
    name: 'counter',
    component: Counter,
  }
]

export default vueSSR(App, { routes })

State management

Pinia/Vuex 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 devalue

Router

It's possible to make changes to the router, use the router property in the callback.

const routes = [
  {
    path: '/',
    name: 'counter',
    component: Counter,
  },
]

export default vueSSR(App, { routes }, ({ router }) => {
  router.beforeEach(async (to, from) => {
    if (
      !isAuthenticated &&
      to.name !== 'Login'
    ) {
      return { name: 'Login' }
    }
  })
})

To customize the router, just return the router instance.

The routes parameter is omitted, because we create a fresh router instance in the method.

export default vueSSR(App, {}, async ({ app }) => {
  const router = createRouter({
    history: import.meta.env.SSR ? createMemoryHistory('/') : createWebHistory('/'),
    routes: [
      {
        path: '/',
        name: 'counter',
        component: Counter,
      },
    ],
  })

  return {
    router,
  }
})

H3

H3 is the underlaying server. During development it injects as an middleware.

The event param is used to access the H3 composables.

NOTE: only works in SSR

import { getRequestURL } from 'h3'

export default vueSSR(App, { routes }, ({ event }) => {
  if (import.meta.env.SSR) {
    console.log(getRequestURL(event)) // "https://example.com/path"
  }

  console.log(event) // undefined
})

In a Vue component, use the useH3Event() composable

import { useH3Event } from 'vite-plugin-vue-ssr'
import { getRequestURL } from 'h3'

if (import.meta.env.SSR) {
  const event = useH3Event()

  console.log(getRequestURL(event)) // "https://example.com/path"
}

See https://h3.unjs.io/utils for more composables.

Teleports

Using Teleport is supported, but requires a little bit of setup. Targeting body is not supported (in SSR), 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