Skip to content
← All questions
Intermediate

What is the difference between controlled and uncontrolled inputs?

ComponentsForms

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>

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>

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" />

The hybrid pattern: lazy v-model

v-model.lazy syncs on change (blur/enter) instead of input, giving you a middle ground:

vue
<template>
  <!-- Updates only when the user leaves the field or presses Enter -->
  <input v-model.lazy="search" />
</template>

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.

Released under the MIT License.