Skip to content
← Todas las preguntas
⚡ Intermedio

¿Cuál es la diferencia entre inputs controlados y no controlados?

🧩 Componentes📝 Formularios🔄 v-model

Un input controlado tiene su valor dirigido por estado reactivo. Cada pulsación de tecla actualiza el estado, y el estado dicta lo que muestra el input. Un input no controlado gestiona su propio valor internamente a través del DOM. Lo lees cuando lo necesitas (al enviar, al perder el foco) en lugar de rastrear cada cambio.

Controlado (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

Cada carácter que escribe el usuario fluye a través de: evento input → actualiza name → Vue re-renderiza → el input muestra el nuevo valor. Siempre tienes el valor actual en name.

No controlado (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

El DOM posee el valor. Solo lo lees cuando lo necesitas.

Cuándo usar cada uno

SituaciónEnfoquePor qué
Validación en tiempo realControladoNecesitas el valor en cada pulsación
Visualización derivada (contador de caracteres, previsualización)ControladoLas propiedades computed dependen del valor
Lógica condicional (deshabilitar botón hasta que sea válido)ControladoEl template necesita acceso reactivo
Formulario simple enviado al pulsar un botónCualquieraEl no controlado es más simple si no necesitas actualizaciones en vivo
Inputs de archivoNo controlado<input type="file"> es de solo lectura, v-model no aplica
Librerías DOM de tercerosNo controladoLa librería gestiona su propio estado DOM

Valores por defecto

Los inputs controlados establecen el valor inicial a través de la ref:

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

Los inputs no controlados usan el atributo HTML value:

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

El patrón híbrido: v-model.lazy

v-model.lazy sincroniza tras eventos change en lugar de input, ofreciendo un punto intermedio. El evento change se dispara cuando el input pierde el foco después de que su valor haya cambiado:

vue
<template>
  <!-- Se actualiza solo cuando el usuario abandona el campo después de modificarlo -->
  <input v-model.lazy="search" />
</template>
Open in Vue Playground

Esto evita el re-renderizado en cada pulsación mientras mantiene el valor en estado reactivo.

En Vue frente a React

En React, esta distinción es más importante porque los inputs no controlados necesitan useRef y los inputs controlados causan re-renderizados de todo el árbol de componentes. En Vue, el sistema de reactividad es granular, así que los inputs controlados solo actualizan las partes del DOM que dependen del valor. La diferencia de rendimiento entre controlados y no controlados es insignificante en Vue. Por defecto, usa v-model salvo que tengas un motivo concreto para no hacerlo.

Ver también: ¿Cómo funciona v-model en componentes personalizados? · ¿Cómo funcionan las template refs? · ¿Cómo manejar formularios complejos?

Referencias

Publicado bajo la licencia MIT.