Skip to content
← All questions
⚡ Intermediate

What is the difference between controlled and uncontrolled inputs?

🧩 Components📝 Forms🔄 v-model

A controlled input has its value driven by reactive state. Every keystroke updates the state, and the state dictates what the input displays. An uncontrolled input manages its own value internally through the DOM. You read it when needed (on submit, on blur) rather than tracking every change.

Controlled (v-model)

vue
<script setup>
import { ref } from 'vue'

const name = ref('')
</script>

<template>
  <input v-model="name" />
  <p>Current value: {{ name }}</p>
</template>
Open in Vue Playground

Every character the user types flows through: input event → update name → Vue re-renders → input shows new value. You always have the current value in name.

Uncontrolled (template ref)

vue
<script setup>
import { useTemplateRef } from 'vue'

const inputEl = useTemplateRef('name-input')

function handleSubmit() {
  const value = inputEl.value?.value
  console.log('Submitted:', value)
}
</script>

<template>
  <input ref="name-input" />
  <button @click="handleSubmit">Submit</button>
</template>
Open in Vue Playground

The DOM owns the value. You only read it when you need it.

When to use which

SituationApproachWhy
Real-time validationControlledYou need the value on every keystroke
Derived display (character count, preview)ControlledComputed properties depend on the value
Conditional logic (disable button until valid)ControlledTemplate needs reactive access
Simple form submitted on button clickEither worksUncontrolled is simpler if you don't need live updates
File inputsUncontrolled<input type="file"> is read-only, v-model doesn't apply
Third-party DOM librariesUncontrolledThe library manages its own DOM state

Default values

Controlled inputs set the initial value through the ref:

ts
const email = ref('user@example.com')

Uncontrolled inputs use the HTML value attribute:

vue
<input ref="email-input" value="user@example.com" />
Open in Vue Playground

The hybrid pattern: lazy v-model

v-model.lazy syncs after change events instead of input events, giving you a middle ground. The change event fires when the input loses focus after its value has changed:

vue
<template>
  <!-- Updates only when the user leaves the field after changing it -->
  <input v-model.lazy="search" />
</template>
Open in Vue Playground

This avoids re-rendering on every keystroke while still keeping the value in reactive state.

In Vue vs React

In React, this distinction is a bigger deal because uncontrolled inputs need useRef and controlled inputs cause re-renders of the entire component tree. In Vue, the reactivity system is fine-grained, so controlled inputs only update the parts of the DOM that depend on the value. The performance difference between controlled and uncontrolled is negligible in Vue. Default to v-model unless you have a specific reason not to.

See also: How does v-model work on custom components? · How do template refs work? · How would you handle complex forms?

References

Released under the MIT License.