The Vue security model relies on framework-enforced output encoding and state boundaries, while requiring developers to actively manage raw HTML, dynamic URL schemes, and template execution. The framework protects applications against default text-based XSS through standard template interpolations, but leaves developers responsible for validating and sanitizing untrusted inputs passed to directives like v-html and dynamic attributes like :href. Security-sensitive surfaces include component props, event payloads, dependency bundling configurations, and browser storage persistence, which must fail closed when encountering malformed or unverified data.
Essential implementation rules
Keep computed getters pure and avoid direct mutation of derived states
Computed getter functions should never mutate reactive state, perform asynchronous operations, or manipulate the DOM, and their return values must be treated as immutable snapshots. Handle side-effecting work in a watch() callback or appropriate lifecycle hook instead.
Enforce prop immutability and validate component boundaries
Treat received props as read-only by avoiding direct mutations or modifications of nested fields, and use custom events carrying updated data for parent-managed state changes. Use object-syntax defineProps() and explicit payload validation before calling emit() to reject malformed input.
Disable production devtools and development debugging flags
Configure release builds to explicitly set __VUE_PROD_DEVTOOLS__ to 'false', disable source maps, and define process.env.NODE_ENV as production to strip devtools hooks, debugging warnings, and internal state inspection capabilities.
Externalize library dependencies and peer dependencies
Configure bundler externalization options to include every package listed in dependencies and peerDependencies when shipping a Vue plugin or component library to prevent unintended inlining.
Prevent untrusted template execution and raw HTML rendering
Never pass untrusted user input directly as Vue component template strings or unsanitized dynamic user content to the v-html directive or innerHTML properties. Use standard template interpolation via {{ }} for safe text rendering.
Bind dynamic styles using secure object syntax
Restrict dynamic style inputs to explicit allowed properties using object syntax bindings instead of allowing user-controlled CSS strings or raw style tags.
Sanitize dynamic URL schemes in attribute bindings
Validate and sanitize dynamically bound URLs before passing them to template attributes such as :href to prevent unsafe URL schemes like javascript: from executing scripts.
Prevent reactivity overhead on large datasets with shallowRef
Opt out of deep reactivity proxy wrapping by using shallowRef() for massive immutable or infrequently mutated datasets to prevent CPU and memory exhaustion.
Validate and sanitize persisted browser storage state
Treat values restored from browser storage or URL fragments as untrusted input by parsing them inside a try...catch, verifying runtime types, and copying only explicitly allowed options into a new object before applying state.
Protect provided reactive state using readonly wrappers
Wrap reactive state exposed through provide() with readonly() to prevent unauthorized or untracked state mutations by descendant injector components, exposing dedicated update functions for controlled modifications.
vue: All Security Cards
Approximately 3,012 tokens
On this card
Category: api contract misuse
Keep computed getters pure and avoid direct mutation of derived states
Use when
Developing computed properties for derived state in Vue components.
Secure rules
Rule 1: Keep computed getter functions side-effect free and pure
Computed getters should never mutate reactive state, perform asynchronous operations, or manipulate the DOM. Handle any side-effecting work in a watch() callback or another appropriate lifecycle hook instead.
<script setup>import { ref, computed, watch } from 'vue'const count = ref(0)/* Pure computed — derives data only */const doubleCount = computed(() => count.value * 2)/* Side-effects belong in a watcher */watch(count, async (newVal) => { // Safe: perform async work when count changes await fetch(`/api/items/${newVal}`)})</script>
Rule 2: Treat computed return values as immutable snapshots
Never mutate the value returned by a computed property. If updates are required, either change the underlying reactive state or create a writable computed property that defines an explicit set function.
<script setup>import { ref, computed } from 'vue'const firstName = ref('Jane')const lastName = ref('Doe')/* Writable computed with getter and setter */const fullName = computed({ get() { return `${firstName.value} ${lastName.value}` }, set(newVal) { ;[firstName.value, lastName.value] = newVal.split(' ') }})/* Avoid: mutating the computed snapshot directly */// fullName.value = 'Will throw a warning unless a setter is provided'</script>
Category: boundary control
Emit events and clone state updates instead of mutating props directly
Use when
When updating component data or passing state modifications from a child component back up to a parent component across the component state boundary.
Secure rules
Rule 1: Keep props immutable and use custom events for updates
Props received by a child component are read-only. Avoid mutating them or any of their nested fields. Instead, emit a custom event carrying the updated data so the parent (the prop owner) performs the state change.
Secure Development Tooling and Production Build Flags in Vue
Use when
Configuring production bundlers and build workflows for Vue applications to prevent exposing development tooling, internal devtools hooks, or raw source maps.
Secure rules
Rule 1: Disable Vue Devtools support and production source maps
In release builds, compile Vue with __VUE_PROD_DEVTOOLS__ set to 'false' and turn build.sourcemap off so that devtools hooks and source maps are excluded from the production bundle.
// vite.config.tsimport { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'export default defineConfig({ plugins: [vue()], define: { // Prevent devtools code from being included in production __VUE_PROD_DEVTOOLS__: 'false' }, build: { // Do not emit source-map files in the production output sourcemap: false }})
Validate Dependency Versions and Externalize Third-Party Modules in Package Configurations
Use when
Building scripts, project generators, or bundler configurations that handle third-party dependencies and package manifests.
Secure rules
Rule 1: Externalize library dependencies and peerDependencies when bundling
When you ship a Vue plugin or component library, configure the bundler’s external option with every package listed in dependencies and peerDependencies.
When defining component templates using Vue APIs such as Vue.createApp({ template: ... }).
Secure rules
Rule 1: Avoid passing untrusted user input directly as Vue component template strings.
Ensure component templates are static or strictly controlled by application developers. Pass dynamic content via template interpolation or dynamic component props instead of string concatenation.
Restrict the use of raw HTML rendering APIs like v-html
Use when
Rendering dynamic or user-supplied content inside Vue component templates where raw HTML injection could occur.
Secure rules
Rule 1: Avoid passing unsanitized dynamic user content to the v-html directive or innerHTML property bindings.
Use standard template interpolation via {{ }} for user text because it automatically escapes HTML using native APIs like textContent. If rendering dynamic HTML is strictly required, ensure the HTML string is properly sanitized first or rendered within a sandboxed environment.
Building dynamic styles and applying CSS properties using Vue style bindings
Secure rules
Rule 1: Bind dynamic styles using object syntax to restrict values to explicit allowed properties.
Avoid allowing user-controlled CSS strings in style bindings or style tags. Restrict dynamic style inputs using Vue style binding object syntax and explicit allowed properties such as color or background.
Enforce Input Contracts on Component Props and Events
Use when
Defining and validating component boundaries such as props and emitted events to reject malformed or out-of-contract input before application processing.
Secure rules
Rule 1: Declare runtime prop requirements for development warnings
Use object-syntax defineProps() declarations with runtime types, required flags, and custom validators to specify the values a component expects. When these requirements are not met, Vue produces console warnings in the development build. These checks report invalid props but do not reject the received values.
Rule 2: Validate event payloads before emitting them
Use defineEmits object syntax to document expected event payloads and report invalid arguments during development. Returning false from an event validator only triggers a warning and does not stop the event from being emitted. To prevent invalid data from reaching parent components, validate the payload explicitly before calling emit().
When rendering dynamic attributes like :href in Vue templates using untrusted data that may contain unsafe URL schemes.
Secure rules
Rule 1: Validate and sanitize dynamically bound URLs before passing them to template attributes to prevent script execution.
While Vue template interpolations using double curly braces escape text automatically, dynamic attribute bindings such as :href do not sanitize unsafe URL schemes like javascript:. Developers must validate and sanitize dynamically bound URLs before passing them to template attributes.
Use shallowRef to prevent reactivity overhead on large datasets
Use when
When processing large data structures or arrays of deeply nested objects in Vue components to prevent CPU and memory exhaustion.
Secure rules
Rule 1: Opt out of deep reactivity for large immutable or infrequently mutated data structures using shallowRef().
Vue’s default reactivity system creates proxy traps for every nested property access, leading to severe CPU and memory overhead on massive datasets. Use shallowRef() to opt out of deep proxy wrapping and maintain fast property access, updating state by replacing the root reference.
import { shallowRef } from 'vue'const bigDataset = shallowRef([ { id: 1, details: { /* deep nested properties */ } }])// Update state by creating a new array reference rather than modifying nested propertiesbigDataset.value = [...bigDataset.value, newItem]
Category: runtime environment hardening
Deploy Production Vue Builds to Disable Development Debugging and Warning Hooks
Use when
Deploying Vue applications to production environments or configuring build pipelines.
Secure rules
Rule 1: Configure build tools to replace process.env.NODE_ENV with ‘production’ or import explicit production bundles to strip devtools integrations and reactivity debugging hooks.
When deploying Vue applications to production, configure your bundler to define process.env.NODE_ENV as ‘production’ or load explicit production files such as vue.global.prod.js to prevent external actors from inspecting internal component states and diagnostic warnings.
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production')})
Category: secret handling
Restrict browser storage to non-sensitive preferences and validate data integrity
Use when
persisting non-sensitive UI settings or application state in browser storage such as localStorage or sessionStorage in a Vue application.
Secure rules
Rule 1: Validate and whitelist persisted state before applying it
Treat values restored from browser storage or URL fragments as untrusted input. Parse them inside a try...catch, verify the expected runtime types, and copy only explicitly allowed option values into a new object before applying the state. TypeScript assertions such as as PersistedState do not perform runtime validation.
Protect Provided Reactive State From Direct Mutation Using Readonly
Use when
Exposing reactive state through Vue’s provide() mechanism to descendant components.
Secure rules
Rule 1: Wrap provided reactive state with readonly() to prevent unauthorized or untracked state mutations by descendant injector components.
When sharing state across component boundaries using provide(), wrap the reactive state using readonly() and explicitly expose dedicated update functions if controlled modification is required.
Keep computed getters pure and avoid direct mutation of derived states
Approximately 393 tokens
Use when
Developing computed properties for derived state in Vue components.
Secure rules
Rule 1: Keep computed getter functions side-effect free and pure
Computed getters should never mutate reactive state, perform asynchronous operations, or manipulate the DOM. Handle any side-effecting work in a watch() callback or another appropriate lifecycle hook instead.
<script setup>import { ref, computed, watch } from 'vue'const count = ref(0)/* Pure computed — derives data only */const doubleCount = computed(() => count.value * 2)/* Side-effects belong in a watcher */watch(count, async (newVal) => { // Safe: perform async work when count changes await fetch(`/api/items/${newVal}`)})</script>
Rule 2: Treat computed return values as immutable snapshots
Never mutate the value returned by a computed property. If updates are required, either change the underlying reactive state or create a writable computed property that defines an explicit set function.
<script setup>import { ref, computed } from 'vue'const firstName = ref('Jane')const lastName = ref('Doe')/* Writable computed with getter and setter */const fullName = computed({ get() { return `${firstName.value} ${lastName.value}` }, set(newVal) { ;[firstName.value, lastName.value] = newVal.split(' ') }})/* Avoid: mutating the computed snapshot directly */// fullName.value = 'Will throw a warning unless a setter is provided'</script>
Emit events and clone state updates instead of mutating props directly
Approximately 240 tokens
Use when
When updating component data or passing state modifications from a child component back up to a parent component across the component state boundary.
Secure rules
Rule 1: Keep props immutable and use custom events for updates
Props received by a child component are read-only. Avoid mutating them or any of their nested fields. Instead, emit a custom event carrying the updated data so the parent (the prop owner) performs the state change.
Secure Development Tooling and Production Build Flags in Vue
Approximately 450 tokens
Use when
Configuring production bundlers and build workflows for Vue applications to prevent exposing development tooling, internal devtools hooks, or raw source maps.
Secure rules
Rule 1: Disable Vue Devtools support and production source maps
In release builds, compile Vue with __VUE_PROD_DEVTOOLS__ set to 'false' and turn build.sourcemap off so that devtools hooks and source maps are excluded from the production bundle.
// vite.config.tsimport { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'export default defineConfig({ plugins: [vue()], define: { // Prevent devtools code from being included in production __VUE_PROD_DEVTOOLS__: 'false' }, build: { // Do not emit source-map files in the production output sourcemap: false }})
Validate Dependency Versions and Externalize Third-Party Modules in Package Configurations
Use when
Building scripts, project generators, or bundler configurations that handle third-party dependencies and package manifests.
Secure rules
Rule 1: Externalize library dependencies and peerDependencies when bundling
When you ship a Vue plugin or component library, configure the bundler’s external option with every package listed in dependencies and peerDependencies.
When defining component templates using Vue APIs such as Vue.createApp({ template: ... }).
Secure rules
Rule 1: Avoid passing untrusted user input directly as Vue component template strings.
Ensure component templates are static or strictly controlled by application developers. Pass dynamic content via template interpolation or dynamic component props instead of string concatenation.
Restrict the use of raw HTML rendering APIs like v-html
Approximately 191 tokens
Use when
Rendering dynamic or user-supplied content inside Vue component templates where raw HTML injection could occur.
Secure rules
Rule 1: Avoid passing unsanitized dynamic user content to the v-html directive or innerHTML property bindings.
Use standard template interpolation via {{ }} for user text because it automatically escapes HTML using native APIs like textContent. If rendering dynamic HTML is strictly required, ensure the HTML string is properly sanitized first or rendered within a sandboxed environment.
Building dynamic styles and applying CSS properties using Vue style bindings
Secure rules
Rule 1: Bind dynamic styles using object syntax to restrict values to explicit allowed properties.
Avoid allowing user-controlled CSS strings in style bindings or style tags. Restrict dynamic style inputs using Vue style binding object syntax and explicit allowed properties such as color or background.
Enforce Input Contracts on Component Props and Events
Approximately 445 tokens
Use when
Defining and validating component boundaries such as props and emitted events to reject malformed or out-of-contract input before application processing.
Secure rules
Rule 1: Declare runtime prop requirements for development warnings
Use object-syntax defineProps() declarations with runtime types, required flags, and custom validators to specify the values a component expects. When these requirements are not met, Vue produces console warnings in the development build. These checks report invalid props but do not reject the received values.
Rule 2: Validate event payloads before emitting them
Use defineEmits object syntax to document expected event payloads and report invalid arguments during development. Returning false from an event validator only triggers a warning and does not stop the event from being emitted. To prevent invalid data from reaching parent components, validate the payload explicitly before calling emit().
When rendering dynamic attributes like :href in Vue templates using untrusted data that may contain unsafe URL schemes.
Secure rules
Rule 1: Validate and sanitize dynamically bound URLs before passing them to template attributes to prevent script execution.
While Vue template interpolations using double curly braces escape text automatically, dynamic attribute bindings such as :href do not sanitize unsafe URL schemes like javascript:. Developers must validate and sanitize dynamically bound URLs before passing them to template attributes.
Use shallowRef to prevent reactivity overhead on large datasets
Approximately 234 tokens
Use when
When processing large data structures or arrays of deeply nested objects in Vue components to prevent CPU and memory exhaustion.
Secure rules
Rule 1: Opt out of deep reactivity for large immutable or infrequently mutated data structures using shallowRef().
Vue’s default reactivity system creates proxy traps for every nested property access, leading to severe CPU and memory overhead on massive datasets. Use shallowRef() to opt out of deep proxy wrapping and maintain fast property access, updating state by replacing the root reference.
import { shallowRef } from 'vue'const bigDataset = shallowRef([ { id: 1, details: { /* deep nested properties */ } }])// Update state by creating a new array reference rather than modifying nested propertiesbigDataset.value = [...bigDataset.value, newItem]
Deploy Production Vue Builds to Disable Development Debugging and Warning Hooks
Approximately 193 tokens
Use when
Deploying Vue applications to production environments or configuring build pipelines.
Secure rules
Rule 1: Configure build tools to replace process.env.NODE_ENV with ‘production’ or import explicit production bundles to strip devtools integrations and reactivity debugging hooks.
When deploying Vue applications to production, configure your bundler to define process.env.NODE_ENV as ‘production’ or load explicit production files such as vue.global.prod.js to prevent external actors from inspecting internal component states and diagnostic warnings.
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production')})
Restrict browser storage to non-sensitive preferences and validate data integrity
Approximately 577 tokens
Use when
persisting non-sensitive UI settings or application state in browser storage such as localStorage or sessionStorage in a Vue application.
Secure rules
Rule 1: Validate and whitelist persisted state before applying it
Treat values restored from browser storage or URL fragments as untrusted input. Parse them inside a try...catch, verify the expected runtime types, and copy only explicitly allowed option values into a new object before applying the state. TypeScript assertions such as as PersistedState do not perform runtime validation.
Protect Provided Reactive State From Direct Mutation Using Readonly
Approximately 202 tokens
Use when
Exposing reactive state through Vue’s provide() mechanism to descendant components.
Secure rules
Rule 1: Wrap provided reactive state with readonly() to prevent unauthorized or untracked state mutations by descendant injector components.
When sharing state across component boundaries using provide(), wrap the reactive state using readonly() and explicitly expose dedicated update functions if controlled modification is required.