Skip to main content Skip to docs navigation

Validación

Proporciona comentarios valiosos a tus usuarios con la validación de formularios HTML5, a través de comportamientos predeterminados del navegador o estilos personalizados y JavaScript.

Somos conscientes de que actualmente los estilos de validación personalizados del lado del cliente y la información sobre herramientas no son accesibles, ya que no están expuestos a tecnologías de asistencia. Mientras trabajamos en una solución, recomendamos usar la opción del lado del servidor o el método de validación predeterminado del navegador.

¿Cómo funciona la validación?

Así es como funciona la validación de formularios con Bootstrap:

  • La validación de formularios HTML se aplica a través de dos pseudoclases de CSS, :invalid y :valid. Se aplica a los elementos <input>, <select> y <textarea>.
  • Bootstrap aplica los estilos :invalid y :valid a la clase padre .was-validated, generalmente aplicada a <form>. De lo contrario, cualquier campo obligatorio sin valor aparecerá como no válido al cargar la página. De esta forma, puedes elegir cuándo activarlos (por lo general, después de intentar enviar el formulario).
  • Para restablecer la apariencia del formulario (por ejemplo, en el caso de envíos de formularios dinámicos usando AJAX), elimina la clase .was-validated del <form> nuevamente después del envío.
  • Como alternativa, las clases .is-invalid y .is-valid se pueden usar en lugar de las pseudoclases para validación del lado del servidor. No requieren una clase padre .was-validated.
  • Debido a restricciones en el funcionamiento de CSS, no podemos (en este momento) aplicar estilos a una <label> que se encuentra antes de un control de formulario en el DOM sin la ayuda de JavaScript personalizado.
  • Todos los navegadores modernos admiten la API de validación de restricciones, una serie de métodos de JavaScript para validar controles de formulario.
  • Los mensajes de feedback pueden utilizar los predeterminados del navegador (diferentes para cada navegador y sin estilo a través de CSS) o nuestros estilos personalizados de feedback con HTML y CSS adicionales.
  • Puedes proporcionar mensajes de validez personalizados con setCustomValidity en JavaScript.

Con esto en mente, considera las siguientes demostraciones para nuestros estilos de validación de formularios personalizados, clases opcionales del lado del servidor y valores predeterminados del navegador.

Estilos personalizados

Para los mensajes de validación de formulario Bootstrap personalizados, deberás agregar el atributo booleano novalidate a tu <form>. Esto deshabilita los tooltips de feedback predeterminados del navegador, pero aún proporciona acceso a las API de validación de formularios en JavaScript. Intenta enviar el siguiente formulario; nuestro JavaScript interceptará el botón de enviar y te transmitirá sus comentarios. Cuando intentes enviar, verás los estilos :invalid y :valid aplicados a los controles de tu formulario.

Los estilos de feedbacks personalizados aplican colores, bordes, estilos de enfoque e iconos de fondo personalizados para comunicar mejor los feedbacks. Los iconos de fondo para <select> solo están disponibles con .form-select, y no con .form-control.

¡Se ve bien!
¡Se ve bien!
@
Por favor, elije un nombre de usuario.
Proporciona una ciudad válida.
Selecciona un estado válido.
Proporciona un código postal válido.
Debe estar de acuerdo antes de enviar.
<form class="row g-3 needs-validation" novalidate>
  <div class="col-md-4">
    <label for="validationCustom01" class="form-label">Nombre</label>
    <input type="text" class="form-control" id="validationCustom01" value="Mark" required>
    <div class="valid-feedback">
      ¡Se ve bien!
    </div>
  </div>
  <div class="col-md-4">
    <label for="validationCustom02" class="form-label">Apellido</label>
    <input type="text" class="form-control" id="validationCustom02" value="Otto" required>
    <div class="valid-feedback">
      ¡Se ve bien!
    </div>
  </div>
  <div class="col-md-4">
    <label for="validationCustomUsername" class="form-label">Nombre de usuario</label>
    <div class="input-group has-validation">
      <span class="input-group-text" id="inputGroupPrepend">@</span>
      <input type="text" class="form-control" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required>
      <div class="invalid-feedback">
        Por favor, elije un nombre de usuario.
      </div>
    </div>
  </div>
  <div class="col-md-6">
    <label for="validationCustom03" class="form-label">Ciudad</label>
    <input type="text" class="form-control" id="validationCustom03" required>
    <div class="invalid-feedback">
      Proporciona una ciudad válida.
    </div>
  </div>
  <div class="col-md-3">
    <label for="validationCustom04" class="form-label">Estado</label>
    <select class="form-select" id="validationCustom04" required>
      <option selected disabled value="">Elige...</option>
      <option>...</option>
    </select>
    <div class="invalid-feedback">
      Selecciona un estado válido.
    </div>
  </div>
  <div class="col-md-3">
    <label for="validationCustom05" class="form-label">Código postal</label>
    <input type="text" class="form-control" id="validationCustom05" required>
    <div class="invalid-feedback">
      Proporciona un código postal válido.
    </div>
  </div>
  <div class="col-12">
    <div class="form-check">
      <input class="form-check-input" type="checkbox" value="" id="invalidCheck" required>
      <label class="form-check-label" for="invalidCheck">
        Acepta los términos y condiciones
      </label>
      <div class="invalid-feedback">
        Debe estar de acuerdo antes de enviar.
      </div>
    </div>
  </div>
  <div class="col-12">
    <button class="btn btn-primary" type="submit">Enviar formulario</button>
  </div>
</form>
// Ejemplo de JavaScript inicial para deshabilitar el envío de formularios si hay campos no válidos
(function () {
  'use strict'

  // Obtener todos los formularios a los que queremos aplicar estilos de validación de Bootstrap personalizados
  var forms = document.querySelectorAll('.needs-validation')

  // Bucle sobre ellos y evitar el envío
  Array.prototype.slice.call(forms)
    .forEach(function (form) {
      form.addEventListener('submit', function (event) {
        if (!form.checkValidity()) {
          event.preventDefault()
          event.stopPropagation()
        }

        form.classList.add('was-validated')
      }, false)
    })
})()

Valores predeterminados del navegador

¿No te interesan los mensajes feedback de validación personalizados o escribir JavaScript para cambiar los comportamientos de los formularios? Todo bien, puedes usar los valores predeterminados del navegador. Intenta enviar el formulario a continuación. Dependiendo de tu navegador y sistema operativo, verás un estilo de retroalimentación ligeramente diferente.

Si bien estos estilos de comentarios no se pueden diseñar con CSS, aún puedes personalizar el texto de comentarios a través de JavaScript.

@
<form class="row g-3">
  <div class="col-md-4">
    <label for="validationDefault01" class="form-label">Nombre</label>
    <input type="text" class="form-control" id="validationDefault01" value="Mark" required>
  </div>
  <div class="col-md-4">
    <label for="validationDefault02" class="form-label">Apellido</label>
    <input type="text" class="form-control" id="validationDefault02" value="Otto" required>
  </div>
  <div class="col-md-4">
    <label for="validationDefaultUsername" class="form-label">Nombre de usuario</label>
    <div class="input-group">
      <span class="input-group-text" id="inputGroupPrepend2">@</span>
      <input type="text" class="form-control" id="validationDefaultUsername"  aria-describedby="inputGroupPrepend2" required>
    </div>
  </div>
  <div class="col-md-6">
    <label for="validationDefault03" class="form-label">Ciudad</label>
    <input type="text" class="form-control" id="validationDefault03" required>
  </div>
  <div class="col-md-3">
    <label for="validationDefault04" class="form-label">Estado</label>
    <select class="form-select" id="validationDefault04" required>
      <option selected disabled value="">Elige...</option>
      <option>...</option>
    </select>
  </div>
  <div class="col-md-3">
    <label for="validationDefault05" class="form-label">Código postal</label>
    <input type="text" class="form-control" id="validationDefault05" required>
  </div>
  <div class="col-12">
    <div class="form-check">
      <input class="form-check-input" type="checkbox" value="" id="invalidCheck2" required>
      <label class="form-check-label" for="invalidCheck2">
        Acepta los términos y condiciones
      </label>
    </div>
  </div>
  <div class="col-12">
    <button class="btn btn-primary" type="submit">Enviar formulario</button>
  </div>
</form>

Lado del servidor

Recomendamos utilizar la validación del lado del cliente, pero en caso de que necesites la validación del lado del servidor, puedes indicar campos de formulario válidos y no válidos con .is-invalid y .is-valid. Ten en cuenta que .invalid-feedback también es compatible con estas clases.

Para los campos no válidos, asegúrate de que el mensaje de error/feedback no válido esté asociado con el campo de formulario relevante utilizando aria-describedby (ten en cuenta que este atributo permite hacer referencia a más de un id, en caso de que el campo ya apunte a un texto de formulario adicional).

Para solucionar problemas con los radios de los bordes, los grupos de inputs requieren una clase .has-validation adicional.

¡Se ve bien!
¡Se ve bien!
@
Por favor, elije un nombre de usuario.
Proporciona una ciudad válida.
Selecciona un estado válido.
Proporciona un código postal válido.
Debe estar de acuerdo antes de enviar.
<form class="row g-3">
  <div class="col-md-4">
    <label for="validationServer01" class="form-label">Nombre</label>
    <input type="text" class="form-control is-valid" id="validationServer01" value="Mark" required>
    <div class="valid-feedback">
      ¡Se ve bien!
    </div>
  </div>
  <div class="col-md-4">
    <label for="validationServer02" class="form-label">Apellido</label>
    <input type="text" class="form-control is-valid" id="validationServer02" value="Otto" required>
    <div class="valid-feedback">
      ¡Se ve bien!
    </div>
  </div>
  <div class="col-md-4">
    <label for="validationServerUsername" class="form-label">Nombre de usuario</label>
    <div class="input-group has-validation">
      <span class="input-group-text" id="inputGroupPrepend3">@</span>
      <input type="text" class="form-control is-invalid" id="validationServerUsername" aria-describedby="inputGroupPrepend3 validationServerUsernameFeedback" required>
      <div id="validationServerUsernameFeedback" class="invalid-feedback">
        Por favor, elije un nombre de usuario.
      </div>
    </div>
  </div>
  <div class="col-md-6">
    <label for="validationServer03" class="form-label">Ciudad</label>
    <input type="text" class="form-control is-invalid" id="validationServer03" aria-describedby="validationServer03Feedback" required>
    <div id="validationServer03Feedback" class="invalid-feedback">
      Proporciona una ciudad válida.
    </div>
  </div>
  <div class="col-md-3">
    <label for="validationServer04" class="form-label">Estado</label>
    <select class="form-select is-invalid" id="validationServer04" aria-describedby="validationServer04Feedback" required>
      <option selected disabled value="">Elige...</option>
      <option>...</option>
    </select>
    <div id="validationServer04Feedback" class="invalid-feedback">
      Selecciona un estado válido.
    </div>
  </div>
  <div class="col-md-3">
    <label for="validationServer05" class="form-label">Código postal</label>
    <input type="text" class="form-control is-invalid" id="validationServer05" aria-describedby="validationServer05Feedback" required>
    <div id="validationServer05Feedback" class="invalid-feedback">
      Proporciona un código postal válido.
    </div>
  </div>
  <div class="col-12">
    <div class="form-check">
      <input class="form-check-input is-invalid" type="checkbox" value="" id="invalidCheck3" aria-describedby="invalidCheck3Feedback" required>
      <label class="form-check-label" for="invalidCheck3">
        Acepta los términos y condiciones
      </label>
      <div id="invalidCheck3Feedback" class="invalid-feedback">
        Debe estar de acuerdo antes de enviar.
      </div>
    </div>
  </div>
  <div class="col-12">
    <button class="btn btn-primary" type="submit">Enviar formulario</button>
  </div>
</form>

Elementos compatibles

Los estilos de validación están disponibles para los siguientes controles y componentes de formulario:

  • <input>s y <textarea>s con .form-control (incluyendo hasta un .form-control en grupos de inputs)
  • <select>s con .form-select
  • .form-checks
Por favor ingresa un mensaje en el área de texto.
Ejemplo de texto de feedback no válido
Otro ejemplo de texto de feedback no válido
Ejemplo de select feedback no válido
Ejemplo de form file feedback no válido
<form class="was-validated">
  <div class="mb-3">
    <label for="validationTextarea" class="form-label">Textarea</label>
    <textarea class="form-control is-invalid" id="validationTextarea" placeholder="Required example textarea" required></textarea>
    <div class="invalid-feedback">
      Por favor ingresa un mensaje en el área de texto.
    </div>
  </div>

  <div class="form-check mb-3">
    <input type="checkbox" class="form-check-input" id="validationFormCheck1" required>
    <label class="form-check-label" for="validationFormCheck1">Marca este checkbox</label>
    <div class="invalid-feedback">Ejemplo de texto de feedback no válido</div>
  </div>

  <div class="form-check">
    <input type="radio" class="form-check-input" id="validationFormCheck2" name="radio-stacked" required>
    <label class="form-check-label" for="validationFormCheck2">Toggle este radio</label>
  </div>
  <div class="form-check mb-3">
    <input type="radio" class="form-check-input" id="validationFormCheck3" name="radio-stacked" required>
    <label class="form-check-label" for="validationFormCheck3">O toggle este otro radio</label>
    <div class="invalid-feedback">Otro ejemplo de texto de feedback no válido</div>
  </div>

  <div class="mb-3">
    <select class="form-select" required aria-label="select example">
      <option value="">Abre este menú</option>
      <option value="1">Uno</option>
      <option value="2">Dos</option>
      <option value="3">Tres</option>
    </select>
    <div class="invalid-feedback">Ejemplo de select feedback no válido</div>
  </div>

  <div class="mb-3">
    <input type="file" class="form-control" aria-label="file example" required>
    <div class="invalid-feedback">Ejemplo de form file feedback no válido</div>
  </div>

  <div class="mb-3">
    <button class="btn btn-primary" type="submit" disabled>Enviar formulario</button>
  </div>
</form>

Tooltips

Si el diseño de tu formulario lo permite, puedes intercambiar las clases .{valid|invalid}-feedback por clases .{valid|invalid}-tooltip para mostrar comentarios de validación en un tooltip con estilo. Asegúrate de tener un padre con position: relative en él para el posicionamiento del tooltip. En el siguiente ejemplo, nuestras clases de columnas ya tienen esto, pero tu proyecto puede requerir una configuración alternativa.

¡Se ve bien!
¡Se ve bien!
@
Elije un nombre de usuario único y válido.
Proporciona una ciudad válida.
Selecciona un estado válido.
Proporciona un código postal válido.
<form class="row g-3 needs-validation" novalidate>
  <div class="col-md-4 position-relative">
    <label for="validationTooltip01" class="form-label">Nombre</label>
    <input type="text" class="form-control" id="validationTooltip01" value="Mark" required>
    <div class="valid-tooltip">
      ¡Se ve bien!
    </div>
  </div>
  <div class="col-md-4 position-relative">
    <label for="validationTooltip02" class="form-label">Apellido</label>
    <input type="text" class="form-control" id="validationTooltip02" value="Otto" required>
    <div class="valid-tooltip">
      ¡Se ve bien!
    </div>
  </div>
  <div class="col-md-4 position-relative">
    <label for="validationTooltipUsername" class="form-label">Nombre de usuario</label>
    <div class="input-group has-validation">
      <span class="input-group-text" id="validationTooltipUsernamePrepend">@</span>
      <input type="text" class="form-control" id="validationTooltipUsername" aria-describedby="validationTooltipUsernamePrepend" required>
      <div class="invalid-tooltip">
        Elije un nombre de usuario único y válido.
      </div>
    </div>
  </div>
  <div class="col-md-6 position-relative">
    <label for="validationTooltip03" class="form-label">Ciudad</label>
    <input type="text" class="form-control" id="validationTooltip03" required>
    <div class="invalid-tooltip">
      Proporciona una ciudad válida.
    </div>
  </div>
  <div class="col-md-3 position-relative">
    <label for="validationTooltip04" class="form-label">Estado</label>
    <select class="form-select" id="validationTooltip04" required>
      <option selected disabled value="">Elige...</option>
      <option>...</option>
    </select>
    <div class="invalid-tooltip">
      Selecciona un estado válido.
    </div>
  </div>
  <div class="col-md-3 position-relative">
    <label for="validationTooltip05" class="form-label">Código postal</label>
    <input type="text" class="form-control" id="validationTooltip05" required>
    <div class="invalid-tooltip">
      Proporciona un código postal válido.
    </div>
  </div>
  <div class="col-12">
    <button class="btn btn-primary" type="submit">Enviar formulario</button>
  </div>
</form>

Sass

Variables

$form-feedback-margin-top:          $form-text-margin-top;
$form-feedback-font-size:           $form-text-font-size;
$form-feedback-font-style:          $form-text-font-style;
$form-feedback-valid-color:         $success;
$form-feedback-invalid-color:       $danger;

$form-feedback-icon-valid-color:    $form-feedback-valid-color;
$form-feedback-icon-valid:          url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'><path fill='#{$form-feedback-icon-valid-color}' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/></svg>");
$form-feedback-icon-invalid-color:  $form-feedback-invalid-color;
$form-feedback-icon-invalid:        url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='#{$form-feedback-icon-invalid-color}'><circle cx='6' cy='6' r='4.5'/><path stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/><circle cx='6' cy='8.2' r='.6' fill='#{$form-feedback-icon-invalid-color}' stroke='none'/></svg>");

Mixins

Dos mixins se combinan juntos, a través de nuestro loop, para generar nuestros estilos de feedback de validación de formularios.

@mixin form-validation-state-selector($state) {
  @if ($state == "valid" or $state == "invalid") {
    .was-validated #{if(&, "&", "")}:#{$state},
    #{if(&, "&", "")}.is-#{$state} {
      @content;
    }
  } @else {
    #{if(&, "&", "")}.is-#{$state} {
      @content;
    }
  }
}

@mixin form-validation-state(
  $state,
  $color,
  $icon,
  $tooltip-color: color-contrast($color),
  $tooltip-bg-color: rgba($color, $form-feedback-tooltip-opacity),
  $focus-box-shadow: 0 0 $input-btn-focus-blur $input-focus-width rgba($color, $input-btn-focus-color-opacity)
) {
  .#{$state}-feedback {
    display: none;
    width: 100%;
    margin-top: $form-feedback-margin-top;
    @include font-size($form-feedback-font-size);
    font-style: $form-feedback-font-style;
    color: $color;
  }

  .#{$state}-tooltip {
    position: absolute;
    top: 100%;
    z-index: 5;
    display: none;
    max-width: 100%; // Contain to parent when possible
    padding: $form-feedback-tooltip-padding-y $form-feedback-tooltip-padding-x;
    margin-top: .1rem;
    @include font-size($form-feedback-tooltip-font-size);
    line-height: $form-feedback-tooltip-line-height;
    color: $tooltip-color;
    background-color: $tooltip-bg-color;
    @include border-radius($form-feedback-tooltip-border-radius);
  }

  @include form-validation-state-selector($state) {
    ~ .#{$state}-feedback,
    ~ .#{$state}-tooltip {
      display: block;
    }
  }

  .form-control {
    @include form-validation-state-selector($state) {
      border-color: $color;

      @if $enable-validation-icons {
        padding-right: $input-height-inner;
        background-image: escape-svg($icon);
        background-repeat: no-repeat;
        background-position: right $input-height-inner-quarter center;
        background-size: $input-height-inner-half $input-height-inner-half;
      }

      &:focus {
        border-color: $color;
        box-shadow: $focus-box-shadow;
      }
    }
  }

  // stylelint-disable-next-line selector-no-qualifying-type
  textarea.form-control {
    @include form-validation-state-selector($state) {
      @if $enable-validation-icons {
        padding-right: $input-height-inner;
        background-position: top $input-height-inner-quarter right $input-height-inner-quarter;
      }
    }
  }

  .form-select {
    @include form-validation-state-selector($state) {
      border-color: $color;

      @if $enable-validation-icons {
        &:not([multiple]):not([size]),
        &:not([multiple])[size="1"] {
          padding-right: $form-select-feedback-icon-padding-end;
          background-image: escape-svg($form-select-indicator), escape-svg($icon);
          background-position: $form-select-bg-position, $form-select-feedback-icon-position;
          background-size: $form-select-bg-size, $form-select-feedback-icon-size;
        }
      }

      &:focus {
        border-color: $color;
        box-shadow: $focus-box-shadow;
      }
    }
  }

  .form-check-input {
    @include form-validation-state-selector($state) {
      border-color: $color;

      &:checked {
        background-color: $color;
      }

      &:focus {
        box-shadow: $focus-box-shadow;
      }

      ~ .form-check-label {
        color: $color;
      }
    }
  }
  .form-check-inline .form-check-input {
    ~ .#{$state}-feedback {
      margin-left: .5em;
    }
  }

  .input-group .form-control,
  .input-group .form-select {
    @include form-validation-state-selector($state) {
      @if $state == "valid" {
        z-index: 1;
      } @else if $state == "invalid" {
        z-index: 2;
      }
      &:focus {
        z-index: 3;
      }
    }
  }
}

Mapa

Este es el mapa Sass de validación de _variables.scss. Sobrescribe o amplía esto para generar estados diferentes o adicionales.

$form-validation-states: (
  "valid": (
    "color": $form-feedback-valid-color,
    "icon": $form-feedback-icon-valid
  ),
  "invalid": (
    "color": $form-feedback-invalid-color,
    "icon": $form-feedback-icon-invalid
  )
);

Los mapas de $form-validation-states pueden contener tres parámetros opcionales para sobrescribir los estilos de tooltips y de enfoque.

Loop

Se utiliza para iterar sobre los valores del mapa $form-validation-states para generar nuestros estilos de validación. Cualquier modificación al mapa de Sass anterior se reflejará en tu CSS compilado a través de este ciclo.

@each $state, $data in $form-validation-states {
  @include form-validation-state($state, $data...);
}

Personalización

Los estados de validación se pueden personalizar a través de Sass con el mapa $form-validation-states. Ubicado en nuestro archivo _variables.scss, este mapa de Sass es cómo generamos los estados de validación predeterminados valid/invalid. Se incluye un mapa anidado para personalizar el color, el ícono, el color de tooltips y la sombra de enfoque de cada estado. Si bien los navegadores no admiten otros estados, aquellos que usan estilos personalizados pueden agregar fácilmente feedbacks de formularios más complejos.

Ten en cuenta que no recomendamos personalizar los valores de $form-validation-states sin modificar también el mixin form-validation-state.