Skip to main content

Vue.js SPA Preset

Preset file: presets/vue-spa.toml

Works with Vue 3 + Vite, Vue 2 + CLI, and Nuxt in CSR mode.

Overview

Vue SPAs mount components into a <div id="app"> element. Vue uses v-cloak directives to hide content until the app is mounted, and scoped CSS generates data-v-* hash attributes on elements.

Key Configuration

[render]
wait_for = "networkidle"
timeout_secs = 15
post_wait_js = """
document.querySelectorAll('[v-cloak]').forEach(el => el.removeAttribute('v-cloak'));
"""

[render.postprocess]
enabled = true
strip_scripts = true
strip_noscript = true
strip_comments = true
strip_event_handlers = true
strip_hydration_attrs = true # Removes data-v-*, data-server-rendered
resolve_lazy_images = true

v-cloak Removal

Vue's v-cloak directive hides elements until the Vue instance is mounted. In rendered output, v-cloak may still be present if Vue's initialization is slightly delayed. The post_wait_js script removes it after rendering completes:

document.querySelectorAll('[v-cloak]').forEach(el => el.removeAttribute('v-cloak'));

Without this, elements with v-cloak and a corresponding CSS rule ([v-cloak] { display: none }) would be invisible in the rendered HTML, even though all content has been rendered into the DOM.

Hydration Attribute Stripping

With strip_hydration_attrs = true, PRISM removes:

AttributePurpose in VueWhy It's Stripped
data-v-*Scoped CSS hash identifiersOnly meaningful with Vue's scoped <style> processing
data-server-renderedMarks SSR-rendered content for hydrationOnly needed for client-side hydration matching

Before:

<div data-v-abc123 data-server-rendered="true">
<p data-v-abc123>Hello World</p>
</div>

After:

<div>
<p>Hello World</p>
</div>

Nuxt CSR Mode

For Nuxt.js in client-side rendering mode (SPA mode), this preset works directly. The route exclusions include /_nuxt/** to skip Nuxt's build assets:

[routes]
exclude = [
"/api/**",
"/_nuxt/**", "/static/**", "/assets/**",
# ... asset extensions
]

If your Nuxt app uses SSR or SSG, PRISM is less necessary since Nuxt already renders HTML server-side. PRISM can still help with pages that have heavy client-side data fetching after initial SSR.

Full Preset

presets/vue-spa.toml
[render]
wait_for = "networkidle"
timeout_secs = 15
post_wait_js = """
document.querySelectorAll('[v-cloak]').forEach(el => el.removeAttribute('v-cloak'));
"""

[render.postprocess]
enabled = true
strip_scripts = true
strip_noscript = true
strip_comments = true
strip_event_handlers = true
strip_hydration_attrs = true
resolve_lazy_images = true

[render.content_validation]
enabled = true
min_text_length = 100
require_title = true
min_html_bytes = 1024

[routes]
include = ["/**"]
exclude = [
"/api/**",
"**/*.js", "**/*.css", "**/*.json", "**/*.xml",
"**/*.png", "**/*.jpg", "**/*.gif", "**/*.svg", "**/*.ico",
"**/*.woff", "**/*.woff2", "**/*.ttf", "**/*.wasm", "**/*.map",
"/_nuxt/**", "/static/**", "/assets/**",
"/manifest.json", "/sw.js", "/robots.txt", "/sitemap.xml",
]