← Controllers

form

Async form submission with loading states and validation.

Usage

<form data-controller="form"
      data-action="submit->form#submit"
      data-form-endpoint-value="https://api.web3forms.com/submit">

  <input type="hidden" name="access_key" value="YOUR_KEY">

  <input type="text" name="name" required>
  <input type="email" name="email" required>
  <textarea name="message" required></textarea>

  <button type="submit" data-form-target="submit">
    <span data-form-target="submitText">Send</span>
    <span data-form-target="loadingText" class="hidden">Sending...</span>
  </button>

  <div data-form-target="success" class="hidden">
    Message sent successfully!
  </div>

  <div data-form-target="error" class="hidden">
    Something went wrong. Please try again.
  </div>
</form>

Targets

Target Purpose
submit Submit button (disabled during submission)
submitText Default button text
loadingText Loading state text
success Success message container
error Error message container

Values

Value Type Description
endpoint String Form submission URL

Source

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["submit", "submitText", "loadingText", "success", "error"];
  static values = { endpoint: String };

  async submit(event) {
    event.preventDefault();

    this.setLoading(true);
    this.hideMessages();

    try {
      const formData = new FormData(this.element);
      const response = await fetch(this.endpointValue, {
        method: "POST",
        body: formData,
      });

      if (response.ok) {
        this.showSuccess();
        this.element.reset();
      } else {
        this.showError();
      }
    } catch (error) {
      this.showError();
    } finally {
      this.setLoading(false);
    }
  }

  setLoading(loading) {
    this.submitTarget.disabled = loading;
    this.submitTextTarget.classList.toggle("hidden", loading);
    this.loadingTextTarget.classList.toggle("hidden", !loading);
  }

  hideMessages() {
    this.successTarget.classList.add("hidden");
    this.errorTarget.classList.add("hidden");
  }

  showSuccess() {
    this.successTarget.classList.remove("hidden");
  }

  showError() {
    this.errorTarget.classList.remove("hidden");
  }
}