⌘I

Enviar formularios con JavaScript (AJAX)

Updated March 23, 2026 ·
howtoform-setup
Also available in:

Disponible en todos los planes

Puedes utilizar @formspree/ajax para enviar tus formularios de Formspree con JavaScript puro, sin escribir tu propia lógica de envío con fetch, Axios o jQuery.

La biblioteca funciona con formularios HTML estándar y añade un manejo declarativo de formularios mediante atributos de datos o una pequeña API de JavaScript. Gestiona el envío del formulario, el estado de carga, los errores de validación y los mensajes de éxito de forma automática.

Cómo funciona

@formspree/ajax te ofrece una forma declarativa de conectar un formulario. En lugar de escuchar manualmente los eventos de envío y escribir tu propio manejo de solicitudes, marcas tu formulario con atributos de datos y lo inicializas con tu ID de formulario de Formspree.

De forma predeterminada, la biblioteca gestiona:

  • el envío del formulario
  • el estado de carga en el botón de envío
  • los errores de validación a nivel de campo
  • los errores a nivel de formulario
  • los mensajes de éxito
  • el estado de campo inválido con aria-invalid="true"

Así es como puedes implementarlo en tu proyecto:

Paso 1: Configurar @formspree-ajax en tu proyecto

Puedes usar @formspree/ajax de dos maneras, según tu configuración:

  • Con un bundler (ESM mediante npm/yarn)
  • Sin un bundler (etiqueta script/CDN)

Opción 1: Usando un bundler (ESM)

Instala la biblioteca:

npm install @formspree/ajax   # via npm, or
yarn add @formspree/ajax      # via yarn

E inicializa tu formulario en JavaScript:

import { initForm } from '@formspree/ajax';

initForm({
  formElement: '#contact-form',
  formId: 'YOUR_FORM_ID',
});

Asegúrate de que el ID de tu elemento de formulario HTML coincida con el ID que pasas a la propiedad formElement. Y reemplaza YOUR_FORM_ID con el ID del formulario que recibes en el panel de Formspree.

Opción 2: Usando una etiqueta script (sin paso de compilación)

Si no estás usando un bundler, añade este fragmento antes de la etiqueta de cierre </body>:

<script>
  window.formspree =
    window.formspree ||
    function () {
      (formspree.q = formspree.q || []).push(arguments);
    };
  formspree(
    'initForm', 
    { formElement: '#contact-form', formId: 'YOUR_FORM_ID' }
  );
</script>
<script src="https://unpkg.com/@formspree/ajax@1" defer></script>

Al igual que con la primera opción, asegúrate de que el ID de tu elemento de formulario HTML coincida con el ID que pasas a la propiedad formElement. Y reemplaza YOUR_FORM_ID con el ID del formulario que recibes en el panel de Formspree.

Paso 2: Crear tu formulario

Una vez que hayas configurado @formspree/ajax en tu proyecto, así es como puedes crear un formulario con él:

<div data-fs-success></div>
<div data-fs-error></div>

<form id="contact-form">
  <label for="email">Email</label>
  <input type="email" id="email" name="email" data-fs-field />
  <span data-fs-error="email"></span>

  <button type="submit" data-fs-submit-btn>Send</button>
</form>

Una vez más, asegúrate de que el valor del atributo id de tu formulario coincida con el ID que pasaste como formId en la llamada a initForm del paso anterior.

La biblioteca enviará automáticamente el formulario a Formspree, deshabilitará el botón de envío mientras la solicitud está en curso, mostrará los errores de validación y mostrará un mensaje de éxito.

Atributos de datos

La biblioteca @formspree-ajax incluye algunos atributos que te permiten controlar cómo se comporta el formulario sin requerir JavaScript adicional.

data-fs-error="fieldName"

Úsalo en un <span> o <div> para mostrar errores de validación a nivel de campo.

<span data-fs-error="email"></span>

Si el elemento está vacío, Formspree inyecta el mensaje de error de la API. Si el elemento ya contiene contenido, ese contenido se muestra u oculta en su lugar.

data-fs-error

Úsalo sin un valor para mostrar errores a nivel de formulario.

<div data-fs-error></div>

Esto es útil para errores generales de envío que no pertenecen a un campo específico.

data-fs-success

Úsalo para mostrar un mensaje de éxito después del envío.

<div data-fs-success></div>

Si el elemento está vacío, se muestra el mensaje predeterminado. Si ya tiene contenido, se utiliza ese contenido en su lugar.

data-fs-field

Úsalo en los campos del formulario que deban recibir aria-invalid="true" cuando la validación falle.

<input type="email" name="email" data-fs-field />

La biblioteca lee el nombre del campo desde el atributo name del elemento.

data-fs-submit-btn

Úsalo en el botón de envío para que se deshabilite durante el envío y se vuelva a habilitar después.

<button type="submit" data-fs-submit-btn>Send</button>

Comportamiento predeterminado

Si no anulas nada, la biblioteca proporciona valores predeterminados sensatos:

  • El botón de envío se deshabilita mientras se envía el formulario.
  • Los errores de campo se muestran dentro de los elementos data-fs-error="fieldName" correspondientes.
  • Los campos inválidos reciben aria-invalid="true".
  • Se muestra un mensaje de éxito en el elemento data-fs-success.
  • Se muestra un mensaje de error a nivel de formulario en el elemento data-fs-error.
  • Si no hay un elemento data-fs-success ni un manejador onSuccess personalizado, el formulario se reemplaza con un mensaje “Thank you!”.
  • Si hay un elemento data-fs-success y no hay un manejador onSuccess personalizado, el formulario se reinicia tras un envío exitoso.
  • Se inyectan estilos predeterminados básicos automáticamente.

Configuración avanzada

La biblioteca también ofrece formas de personalizar aún más el comportamiento y la apariencia de tu formulario.

Añadir datos adicionales a cada envío

Puedes usar la opción data para agregar valores adicionales a cada envío de formulario. Los valores pueden ser:

  • cadenas estáticas
  • funciones síncronas
  • funciones asíncronas

Cualquier valor undefined se omite. Así es como se usa:

initForm({
  formElement: '#my-form',
  formId: 'YOUR_FORM_ID',

  // This data will be added to every submission
  data: {
    source: 'landing-page',
    referrer: () => document.referrer || undefined,
    sessionId: async () => await fetchSessionId(),
  },
});

Es útil para adjuntar metadatos como nombres de campañas, fuentes de referencia o IDs de sesión sin tener que añadir inputs ocultos a tu HTML.

Callbacks del ciclo de vida

Puedes engancharte a las distintas etapas del ciclo de vida del envío.

initForm({
  formElement: '#contact-form',
  formId: 'YOUR_FORM_ID',

  // Called when the form is initialized
  onInit: (context) => {
    console.log('Form initialized');
  },

  // Called before submission is sent.
  onSubmit: (context) => {
    console.log('Submitting form');
  },

  // Called on successful submission.
  onSuccess: (context, result) => {
    console.log('Submission succeeded', result);
  },

  // Called if validation errors are received from Formspree
  onError: (context, error) => {
    console.log('Validation error', error);
  },

  // Called if unexpected errors are received (e.g., network failure).
  onFailure: (context, error) => {
    console.log('Unexpected error', error);
  },
});

El objeto context contiene el formulario actual (HTMLFormElement), los detalles del endpoint, el cliente y el objeto de configuración que pasaste a initForm.

Estos callbacks se pueden usar para:

  • Seguimiento de eventos (analítica, conversiones, abandonos)
  • Mostrar UI personalizada (spinners, toasts, banners)
  • Redirigir tras un envío exitoso
  • Registrar o depurar solicitudes y fallos
  • Manejo personalizado de errores de validación o a nivel de formulario
  • Ejecutar efectos secundarios antes o después del envío (por ejemplo, guardar estado, activar workflows)

Renderizado personalizado

Puedes anular el comportamiento predeterminado de la UI proporcionando métodos de renderizado personalizados en initForm(). Esto es útil si quieres tener control total sobre cómo aparecen los estados de carga, los errores y los mensajes de éxito.

initForm({
  formElement: '#contact-form',
  formId: 'YOUR_FORM_ID',

  enable: (context) => {},
  disable: (context) => {},
  renderFieldErrors: (context, error) => {},
  renderSuccess: (context, message) => {},
  renderFormError: (context, message) => {},
});

Métodos disponibles

MétodoCuándo se ejecutaUsos comunes
disable(context)Cuando comienza el envíoAñadir estado de carga, deshabilitar botones/inputs
enable(context)Después de que el envío se completaEliminar spinners, volver a habilitar la UI
renderFieldErrors(context, error)En errores de validaciónUI de error personalizada, errores agrupados, mensajes toast
renderSuccess(context, message)En envíos exitososReemplazar el formulario, mostrar toast, redirigir
renderFormError(context, message)En errores a nivel de formularioBanners de error, alertas, mensajería centralizada

Así es como se usan:

initForm({
  formElement: '#contact-form',
  formId: 'YOUR_FORM_ID',

  disable: ({ form }) => form.classList.add('loading'),
  enable: ({ form }) => form.classList.remove('loading'),

  renderSuccess: ({ form }, message) => {
    form.innerHTML = `<p>${message}</p>`;
  },
});

Estilos

La biblioteca incluye estilos predeterminados para errores, campos inválidos y mensajes de éxito. Puedes anularlos con tu propio CSS.

[data-fs-error] {
  color: #dc3455;
}

[data-fs-field][aria-invalid='true'] {
  border-color: #dc3455;
}

[data-fs-success][data-fs-active] {
  background: #de4dda;
}

[data-fs-error=''][data-fs-active] {
  background: #f87dda;
  color: #72c124;
}

Limpieza

initForm() devuelve un handle con un método destroy(). Puedes usarlo si necesitas limpiar el comportamiento del formulario más adelante.

const handle = initForm({
  formElement: '#contact-form',
  formId: 'YOUR_FORM_ID',
});

// Clean up when needed
handle.destroy();

Ejemplo completo en ESM

Aquí tienes un ejemplo completo usando un formulario HTML simple y @formspree/ajax con un bundler. Primero, inicializa el formulario con tu ID de formulario de Formspree:

import { initForm } from '@formspree/ajax';

initForm({
  formElement: '#contact-form',
  formId: 'YOUR_FORM_ID',
});

Luego, crea el formulario HTML con los atributos de datos descritos arriba:

<div data-fs-success></div>
<div data-fs-error></div>

<form id="contact-form">
  <label for="email">Email</label>
  <input type="email" id="email" name="email" data-fs-field />
  <span data-fs-error="email"></span>

  <label for="message">Message</label>
  <textarea id="message" name="message" data-fs-field></textarea>
  <span data-fs-error="message"></span>

  <button type="submit" data-fs-submit-btn>Send</button>
</form>

Ejemplo con etiqueta script

Si quieres usar la biblioteca sin un paso de compilación, puedes inicializarla directamente en el navegador:

<div data-fs-success></div>
<div data-fs-error></div>

<form id="my-form">
  <label for="email">Email</label>
  <input type="email" id="email" name="email" data-fs-field />
  <span data-fs-error="email"></span>

  <label for="message">Message</label>
  <textarea id="message" name="message" data-fs-field></textarea>
  <span data-fs-error="message"></span>

  <button type="submit" data-fs-submit-btn>Submit</button>
</form>

<script>
  window.formspree =
    window.formspree ||
    function () {
      (formspree.q = formspree.q || []).push(arguments);
    };

  formspree('initForm', {
    formElement: '#my-form',
    formId: 'YOUR_FORM_ID',
  });
</script>
<script src="https://unpkg.com/@formspree/ajax@1" defer></script>

Migrar desde código AJAX manual

Si previamente has enviado formularios con código personalizado de fetch, Axios o jQuery, @formspree/ajax te ofrece una alternativa más simple. En lugar de hacer manualmente:

  • escuchar eventos de envío
  • serializar los datos del formulario
  • enviar solicitudes
  • analizar los errores de validación
  • actualizar el DOM para los estados de éxito y fallo

Puedes inicializar la biblioteca una vez y dejar que se encargue de esas piezas por ti.

Nota sobre frameworks

Esta biblioteca está diseñada para JavaScript estándar y formularios HTML estándar. Si estás usando React, consulta nuestra biblioteca de React en su lugar.