### Run Multi-Page Examples with Store
Source: https://github.com/antfu-collective/vite-ssg/blob/main/examples/README.md
Commands to develop, build, and serve the multi-page examples that include store functionality.
```bash
pnpm example:store:dev
pnpm example:store:build
pnpm example:store:serve
```
--------------------------------
### Run Multi-Page Examples
Source: https://github.com/antfu-collective/vite-ssg/blob/main/examples/README.md
Commands to develop, build, and serve the multi-page examples.
```bash
pnpm example:dev
pnpm example:build
pnpm example:serve
```
--------------------------------
### Run Single-Page Example
Source: https://github.com/antfu-collective/vite-ssg/blob/main/examples/README.md
Commands to develop, build, and serve the single-page example.
```bash
pnpm example:single:dev
pnpm example:single:build
pnpm example:single:serve
```
--------------------------------
### Install vite-ssg
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Install vite-ssg and its peer dependencies using npm. Vue-router and @unhead/vue are recommended for routing and head management.
```bash
npm i -D vite-ssg vue-router @unhead/vue
```
--------------------------------
### Clone and Build Vite-SSG Project
Source: https://github.com/antfu-collective/vite-ssg/blob/main/examples/README.md
Clone the repository, install dependencies, and build the entire project.
```bash
git clone git@github.com:antfu/vite-ssg.git
cd vite-ssg
pnpm install
pnpm build
```
--------------------------------
### Install beasties for Critical CSS
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Install the 'beasties' package as a dev dependency to enable automatic critical CSS generation.
```bash
npm i -D beasties
```
--------------------------------
### Multi-page App Entry Point with ViteSSG
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Use this for multi-page applications with vue-router. It replaces the standard `createApp` export and handles both SSR and client-side hydration. The setup callback allows for custom logic like state management.
```typescript
// src/main.ts
import { ViteSSG } from 'vite-ssg'
import { createPinia } from 'pinia'
import routes from '~pages' // generated by vite-plugin-pages or unplugin-vue-router
import App from './App.vue'
export const createApp = ViteSSG(
// Root Vue component
App,
// vue-router options (history is injected automatically)
{ routes, base: import.meta.env.BASE_URL },
// Setup callback — runs on both SSR and client
({ app, router, initialState, onSSRAppRendered }) => {
const pinia = createPinia()
app.use(pinia)
if (import.meta.env.SSR) {
// Capture Pinia state after async rendering completes
onSSRAppRendered(() => {
initialState.pinia = pinia.state.value
})
}
else {
// Rehydrate from the serialized state injected into the HTML
pinia.state.value = initialState.pinia ?? {}
}
router.beforeEach((to, _from, next) => {
// Example: lazy-initialize a store before each navigation
const store = useRootStore(pinia)
if (!store.ready)
store.initialize()
next()
})
},
// Client-side options
{
rootContainer: '#app',
// Custom state deserializer (e.g. @nuxt/devalue)
transformState(state) {
return import.meta.env.SSR ? devalue(state) : state
},
},
)
```
--------------------------------
### Single-page App Entry Point with ViteSSG
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Use this for single-page applications without vue-router. It simplifies the setup by omitting router options. The context object provides `app`, `head`, and `initialState`.
```typescript
// src/main.ts
import { ViteSSG } from 'vite-ssg/single-page'
import { createPinia } from 'pinia'
import App from './App.vue'
import { useRootStore } from './store/root'
export const createApp = ViteSSG(
App,
({ app, initialState }) => {
const pinia = createPinia()
app.use(pinia)
if (import.meta.env.SSR) {
initialState.pinia = pinia.state.value
}
else {
pinia.state.value = initialState.pinia ?? {}
}
// Initialize store immediately (no router guards needed)
useRootStore(pinia).initialize()
},
)
```
--------------------------------
### Post-Build Callback for Sitemap Generation
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Execute a function after all routes have been rendered and build artifacts cleaned up. This example generates a sitemap.xml file.
```typescript
// vite.config.ts
import { writeFile } from 'node:fs/promises'
export default defineConfig({
ssgOptions: {
async onFinished() {
// Generate a simple sitemap after all pages are written
const routes = ['/','/ about', '/blog', '/contact']
const sitemap = routes
.map(r => `https://example.com${r}`)
.join('\n')
await writeFile(
'dist/sitemap.xml',
`
${sitemap}
`,
)
console.log('Sitemap written to dist/sitemap.xml')
},
},
})
```
--------------------------------
### ViteSSG() — Multi-page app entry point
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
This function replaces the standard `createApp` export for applications using `vue-router`. It handles both SSR and client-side hydration, accepting the root component, router options, a setup callback, and client-side options.
```APIDOC
## ViteSSG() — Multi-page app entry point
Replaces the standard `createApp` export in `src/main.ts` for applications using `vue-router`. Accepts the root component, router options, an optional setup callback receiving the full `ViteSSGContext`, and optional client-side options. The returned `createApp` factory is called by the build process for every route and by the browser for hydration.
```ts
// src/main.ts
import { ViteSSG } from 'vite-ssg'
import { createPinia } from 'pinia'
import routes from '~pages' // generated by vite-plugin-pages or unplugin-vue-router
import App from './App.vue'
export const createApp = ViteSSG(
// Root Vue component
App,
// vue-router options (history is injected automatically)
{ routes, base: import.meta.env.BASE_URL },
// Setup callback — runs on both SSR and client
({ app, router, initialState, onSSRAppRendered }) => {
const pinia = createPinia()
app.use(pinia)
if (import.meta.env.SSR) {
// Capture Pinia state after async rendering completes
onSSRAppRendered(() => {
initialState.pinia = pinia.state.value
})
}
else {
// Rehydrate from the serialized state injected into the HTML
pinia.state.value = initialState.pinia ?? {}
}
router.beforeEach((to, _from, next) => {
// Example: lazy-initialize a store before each navigation
const store = useRootStore(pinia)
if (!store.ready)
store.initialize()
next()
})
},
// Client-side options
{
rootContainer: '#app',
// Custom state deserializer (e.g. @nuxt/devalue)
transformState(state) {
return import.meta.env.SSR ? devalue(state) : state
},
},
)
```
```
--------------------------------
### Create Vue App with ViteSSG
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Use ViteSSG to create your Vue application. This setup is required for server-side rendering and static site generation. Ensure your main entry file exports createApp.
```typescript
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
// `export const createApp` is required instead of the original `createApp(App).mount('#app')`
export const createApp = ViteSSG(
// the root component
App,
// vue-router options
{ routes },
// function to have custom setups
({ app, router, routes, isClient, initialState }) => {
// install plugins etc.
},
)
```
--------------------------------
### Single Page Application SSG
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
For single-page SSG without vue-router, import 'vite-ssg/single-page'. Ensure you have @unhead/vue installed.
```typescript
import { ViteSSG } from 'vite-ssg/single-page'
import App from './App.vue'
// `export const createApp` is required instead of the original `createApp(App).mount('#app')`
export const createApp = ViteSSG(App)
```
--------------------------------
### Customizing State Serialization with @nuxt/devalue
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Use the `transformState` option to customize how state is serialized and deserialized, for example, with `@nuxt/devalue`.
```typescript
import devalue from '@nuxt/devalue'
import { ViteSSG } from 'vite-ssg'
// ...
import App from './App.vue'
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, initialState }) => {
// ...
},
{
transformState(state) {
return import.meta.env.SSR ? devalue(state) : state
},
},
)
```
--------------------------------
### Build Project with `vite-ssg build` CLI
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
The `vite-ssg build` command replaces `vite build` and accepts flags that map directly to `ViteSSGOptions`. This allows for configuration of script loading, mocking, build modes, and alternative Vite config files directly from the command line.
```json
// package.json
{
"scripts": {
"dev": "vite",
"build": "vite-ssg build",
// With options
"build:prod": "vite-ssg build --script async --mode production",
"build:nested": "vite-ssg build --script defer",
// Use an alternative Vite config file
"build:staging": "vite-ssg build -c vite.staging.config.ts --base /staging/"
}
}
```
```bash
# Available CLI flags
vite-ssg build [options]
--script Script loading: sync | async | defer | "async defer"
--mock Mock browser globals (window, document) during SSR build
--mode Vite mode (default: production)
-c, --config Path to an alternative vite.config file
-b, --base Public base path for all assets and routes
-h, --help Show help
```
--------------------------------
### Integrating Pinia with Vite SSG Initial State
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Adapt your main file to use Pinia with Vite SSG. Initial state is serialized and restored via `initialState.pinia`.
```typescript
import { createPinia } from 'pinia'
import routes from 'virtual:generated-pages'
// main.ts
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
// use any store you configured that you need data from on start-up
import { useRootStore } from './store/root'
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, initialState }) => {
const pinia = createPinia()
app.use(pinia)
if (import.meta.env.SSR)
initialState.pinia = pinia.state.value
else
pinia.state.value = initialState.pinia || {}
router.beforeEach((to, from, next) => {
const store = useRootStore(pinia)
if (!store.ready)
// perform the (user-implemented) store action to fill the store's state
store.initialize()
next()
})
},
)
```
--------------------------------
### Integrating Vuex with Vite SSG Initial State
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Adapt your main file to use Vuex with Vite SSG. Initial state is serialized and restored via `initialState.store`.
```typescript
import routes from 'virtual:generated-pages'
// main.ts
import { ViteSSG } from 'vite-ssg'
import { createStore } from 'vuex'
import App from './App.vue'
// Normally, you should definitely put this in a separate file
// in order to be able to use it everywhere
const store = createStore({
// ...
})
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, initialState }) => {
app.use(store)
if (import.meta.env.SSR)
initialState.store = store.state
else
store.replaceState(initialState.store)
router.beforeEach((to, from, next) => {
// perform the (user-implemented) store action to fill the store's state
if (!store.getters.ready)
store.dispatch('initialize')
next()
})
},
)
```
--------------------------------
### Configure build script in package.json
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Update the 'build' script in your package.json to use 'vite-ssg build' instead of 'vite build'. You can also specify a custom Vite config file.
```json
{
"scripts": {
"dev": "vite",
"build": "vite build"
"build": "vite-ssg build"
// OR if you want to use another vite config file
"build": "vite-ssg build -c another-vite.config.ts"
}
}
```
--------------------------------
### Configure Vite SSG Options
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Set global SSG behavior like script loading strategy, output directory style, HTML formatting, concurrency, and mock browser globals.
```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [Vue()],
ssgOptions: {
// Script tag loading strategy for generated HTML
script: 'async', // 'sync' | 'async' | 'defer' | 'async defer'
// Output filename style
dirStyle: 'nested', // 'flat' (default): /foo.html | 'nested': /foo/index.html
// Prettify or minify the generated HTML files
formatting: 'prettify', // 'none' | 'minify' | 'prettify'
// Number of routes rendered in parallel
concurrency: 20,
// Mock browser globals for libraries that need window/document at build time
mock: false,
// Disable critical CSS inlining (beasties)
beastiesOptions: false,
// Custom beasties options when critical CSS is enabled
// beastiesOptions: { preload: 'media' },
// Custom entry file (defaults to src/main.ts detected from index.html)
entry: 'src/main.ts',
// Root container element id (default: 'app')
rootContainerId: 'app',
},
})
```
--------------------------------
### Configure beasties options in vite.config.ts
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Customize critical CSS generation by passing options to `ssgOptions.beastiesOptions` in your vite.config.ts file. Refer to the beasties documentation for available options.
```typescript
// vite.config.ts
export default defineConfig({
ssgOptions: {
beastiesOptions: {
// E.g., change the preload strategy
preload: 'media',
// Other options: https://github.com/danielroe/beasties#usage
},
},
})
```
--------------------------------
### Serialize SSR State with `initialState`
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
The `initialState` option allows you to pass a plain object that is serialized into `window.__INITIAL_STATE__` in the HTML and deserialized on the client. This can eliminate redundant API calls on the first load by embedding fetched data directly into the initial HTML.
```typescript
// src/main.ts — manual initialState usage (without a store)
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
export const createApp = ViteSSG(
App,
{ routes },
async ({ app, initialState }) => {
if (import.meta.env.SSR) {
// Fetch data at build time and embed it in the HTML
const res = await fetch('https://api.example.com/config')
initialState.config = await res.json()
// → serialized as window.__INITIAL_STATE__ = '{"config":{...}}'
}
else {
// Read from the hydrated state — no network request needed
const config = initialState.config
app.provide('appConfig', config)
}
},
)
```
--------------------------------
### Document Head Management with useHead
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Manage document head elements like titles and meta tags using the `useHead` composable from @unhead/vue. This is automatically handled by Vite SSG.
```html
```
--------------------------------
### ViteSSG() — Single-page app entry point
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
This version of `ViteSSG` is imported from `vite-ssg/single-page` for applications that render only an index page without `vue-router`. The context object provides `app`, `head`, and `initialState` but omits router-related properties.
```APIDOC
## ViteSSG() — Single-page app entry point
Imported from `vite-ssg/single-page` for applications that render only an index page without `vue-router`. The signature omits router options; the context object provides `app`, `head`, and `initialState` but no `router` or `routes`.
```ts
// src/main.ts
import { ViteSSG } from 'vite-ssg/single-page'
import { createPinia } from 'pinia'
import App from './App.vue'
import { useRootStore } from './store/root'
export const createApp = ViteSSG(
App,
({ app, initialState }) => {
const pinia = createPinia()
app.use(pinia)
if (import.meta.env.SSR) {
initialState.pinia = pinia.state.value
}
else {
pinia.state.value = initialState.pinia ?? {}
}
// Initialize store immediately (no router guards needed)
useRootStore(pinia).initialize()
},
)
```
```
--------------------------------
### Configure beasties in vite.config.ts
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Optionally configure `beasties` within `vite.config.ts` to control critical CSS inlining behavior. Options include `preload`, `inlineFonts`, and `pruneSource`. Set `beastiesOptions` to `false` to disable critical CSS entirely.
```typescript
export default defineConfig({
ssgOptions: {
beastiesOptions: {
// Preload remaining CSS non-blocking after the critical CSS is inlined
preload: 'media', // 'body' | 'media' | 'swap' | 'js' | 'js-lazy'
// Inline all fonts referenced in critical CSS
inlineFonts: true,
// Prune source CSS map references
pruneSource: false,
},
// Set to false to disable critical CSS entirely
// beastiesOptions: false,
},
})
```
--------------------------------
### Setting and Hydrating Initial State in Vite SSG
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Set initial state during SSR and access it on the client. This avoids refetching data on the client-side.
```typescript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// src/main.ts
import { ViteSSG } from 'vite-ssg'
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, routes, isClient, initialState }) => {
// ...
if (import.meta.env.SSR) {
// Set initial state during server side
initialState.data = { cats: 2, dogs: 3 }
}
else {
// Restore or read the initial state on the client side in the browser
console.log(initialState.data) // => { cats: 2, dogs: 3 }
}
// ...
},
)
```
--------------------------------
### Configure Vite SSG Script Option
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Configure the `script` option in `vite.config.js` to control how scripts are handled during the build.
```javascript
// vite.config.js
export default {
plugins: [],
ssgOptions: {
script: 'async',
},
}
```
--------------------------------
### Dynamically Select Routes with Vite Env Access
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Fetch dynamic routes from an API and include them in the build. This method requires exporting `includedRoutes` from `src/main.ts` to access Vite's environment variables.
```typescript
// src/main.ts — dynamic routes with Vite env access
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
export const createApp = ViteSSG(App, { routes })
// Exported alongside createApp — vite-ssg picks it up automatically
export async function includedRoutes(paths: string[], routes: RouteRecordRaw[]) {
const api = new BlogApiClient(import.meta.env.VITE_API_KEY)
const slugs = await api.fetchSlugs()
return routes.flatMap((route) => {
if (route.name === 'BlogPost')
return slugs.map(slug => `/blog/${slug}`)
return route.path
})
}
```
--------------------------------
### Document Head Management with @unhead/vue
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Use `useHead()` inside any component or page for automatic server-side injection and client-side merging of document head elements. Ensure `@unhead/vue` is bundled and initialized.
```vue
{{ post.title }}
{{ post.excerpt }}
```
--------------------------------
### Vite Configuration for @nuxt/devalue
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Add a resolve alias to your Vite config if you encounter `require` errors with `@nuxt/devalue`.
```typescript
// vite.config.ts
// ...
export default defineConfig({
resolve: {
alias: {
'@nuxt/devalue': '@nuxt/devalue/dist/devalue.js',
},
},
// ...
})
```
--------------------------------
### PWA Integration with vite-plugin-pwa
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Integrate `vite-plugin-pwa` to automatically update the service worker precache manifest after all pages are rendered. This ensures the manifest reflects the final set of HTML files.
```typescript
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
Vue(),
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
},
manifest: {
name: 'My App',
short_name: 'App',
theme_color: '#ffffff',
},
}),
],
ssgOptions: {
script: 'async',
formatting: 'minify',
},
})
```
--------------------------------
### Conditional Code for Client/Server Builds
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Wrap code that should only run on the client or server within `import.meta.env.SSR` checks. This allows Rollup to tree-shake code effectively.
```typescript
if (import.meta.env.SSR) {
// your server code will be removed in the client build
}
else {
// your client code will be removed in the server build
}
```
--------------------------------
### Export includedRoutes from Server Entry
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Export the `includedRoutes` hook from your server entry file (e.g., `main.ts`) to access Vite-managed environment variables for route generation.
```typescript
// main.ts
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, initialState }) => {
// ...
},
)
export async function includedRoutes(paths, routes) {
// Sensitive key is managed by Vite - this would not be available inside
// vite.config.js as it runs before the environment has been populated.
const apiClient = new MyApiClient(import.meta.env.MY_API_KEY)
return Promise.all(
routes.flatMap(async (route) => {
return route.name === 'Blog'
? (await apiClient.fetchBlogSlugs()).map(slug => `/blog/${slug}`)
: route.path
}),
)
}
```
--------------------------------
### Render Components Only on Client with ``
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
The `` component prevents rendering its content during SSR. The default slot is rendered only after the app mounts in the browser, while the `#placeholder` slot is visible on the server and during hydration.
```vue
Dashboard
Last updated: {{ lastUpdated }}
```
--------------------------------
### ClientOnly Component Usage
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Use the ClientOnly component to render specific parts of your application only on the client-side. It supports a placeholder slot for server-side rendering.
```html
```
--------------------------------
### Handle Async Components with onSSRAppRendered
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Use `onSSRAppRendered` to ensure all async operations are complete during the initial render when using features like Vue's Suspense.
```typescript
const { app, router, initialState, isClient, onSSRAppRendered } = ctx
const pinia = createPinia()
app.use(pinia)
if (import.meta.env.SSR) {
onSSRAppRendered(() => {
initialState.pinia = pinia.state.value
})
}
else {
pinia.state.value = (initialState.pinia) || {}
}
```
--------------------------------
### Generate Custom Routes with includedRoutes Hook
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Use the `includedRoutes` hook in `vite.config.js` to dynamically generate route paths based on existing routes and data.
```javascript
// vite.config.js
export default {
plugins: [],
ssgOptions: {
includedRoutes(paths, routes) {
// use original route records
return routes.flatMap((route) => {
return route.name === 'Blog'
? myBlogSlugs.map(slug => `/blog/${slug}`)
: route.path
})
},
},
}
```
--------------------------------
### Filter Routes Statically
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Control which routes are rendered to HTML by filtering paths directly in vite.config.ts. This method does not have access to Vite's environment variables.
```typescript
// vite.config.ts — static filtering
export default defineConfig({
ssgOptions: {
includedRoutes(paths, routes) {
// Exclude any path containing 'admin'
return paths.filter(p => !p.includes('admin'))
},
},
})
```
--------------------------------
### Capture Async State with `onSSRAppRendered`
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Use `onSSRAppRendered` to register a callback that executes after Vue has fully rendered the component tree, including resolved Suspense boundaries. This is crucial for capturing state populated by async components after the initial render, ensuring it's correctly serialized.
```typescript
// src/main.ts
import { ViteSSG } from 'vite-ssg'
import { createPinia } from 'pinia'
import App from './App.vue'
export const createApp = ViteSSG(App, { routes }, ({ app, initialState, onSSRAppRendered }) => {
const pinia = createPinia()
app.use(pinia)
if (import.meta.env.SSR) {
// Wait until all async setup() / Suspense trees have settled
onSSRAppRendered(() => {
// Pinia stores populated by async components are now stable
initialState.pinia = pinia.state.value
})
}
else {
pinia.state.value = initialState.pinia ?? {}
}
})
```
--------------------------------
### Customize HTML Output Filename with `htmlFileName`
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Use `htmlFileName` to override the default HTML output filename for specific routes. This is particularly useful on Windows for handling catch-all routes generated by `unplugin-vue-router` that contain invalid filename characters.
```typescript
// vite.config.ts
export default defineConfig({
ssgOptions: {
htmlFileName(filename) {
// Rename the catch-all route file produced by unplugin-vue-router on Windows
// Default: ":all(.*).html" → safe: "_catchall.html"
if (filename.includes(':'))
return filename.replace(/:[^/]+/g, '_catchall')
return undefined // keep default for all other routes
},
},
})
```
--------------------------------
### Transform HTML Before Page Rendering
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Modify the raw `index.html` before Vue renders the page for a specific route. Useful for injecting per-route meta tags or feature flags.
```typescript
// vite.config.ts
export default defineConfig({
ssgOptions: {
async onBeforePageRender(route, indexHTML, appCtx) {
// Inject a per-route CSP nonce into the HTML template
const nonce = generateNonce()
return indexHTML.replace(
'',
``,
)
},
},
})
```
--------------------------------
### Transform Final Rendered HTML
Source: https://context7.com/antfu-collective/vite-ssg/llms.txt
Modify the `renderedHTML` after Vue's SSR render and head injection for each route. The returned string replaces the output written to disk.
```typescript
// vite.config.ts
export default defineConfig({
ssgOptions: {
async onPageRendered(route, renderedHTML, appCtx) {
// Add a canonical link tag based on the current route
const canonical = `https://example.com${route}`
return renderedHTML.replace(
'',
`
`,
)
},
},
})
```
--------------------------------
### Exclude Routes with includedRoutes Hook
Source: https://github.com/antfu-collective/vite-ssg/blob/main/README.md
Use the `includedRoutes` hook in `vite.config.js` to filter out specific route paths from being rendered.
```javascript
// vite.config.js
export default {
plugins: [],
ssgOptions: {
includedRoutes(paths, routes) {
// exclude all the route paths that contains 'foo'
return paths.filter(i => !i.includes('foo'))
},
},
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.