Skip to main content
Build a basic contact form using Alpine.js that submits directly to PipedForm using fetch, with reactive state managed entirely through x-data.

Basic Example

<div
  x-data="{
    status: 'idle',
    message: '',
    name: '',
    email: '',
    formMessage: '',
    async onSubmit() {
      this.status = 'loading';

      const res = await fetch('https://pipedform.com/f/{form_id}', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
        },
        body: JSON.stringify({
          name: this.name,
          email: this.email,
          message: this.formMessage,
        }),
      });

      const data = await res.json();

      if (res.ok) {
        this.status = 'success';
        this.message = 'Form submitted successfully';
      } else {
        this.status = 'error';
        this.message = data.message;
      }
    }
  }"
>
  <form @submit.prevent="onSubmit">
    <input name="name" x-model="name" placeholder="John" required />
    <input
      type="email"
      name="email"
      x-model="email"
      placeholder="[email protected]"
      required
    />
    <textarea
      name="message"
      x-model="formMessage"
      placeholder="Enter your message..."
      required
    ></textarea>

    <p x-show="status === 'error'" style="color: red" x-text="message"></p>
    <p x-show="status === 'success'" style="color: green" x-text="message"></p>

    <button type="submit" x-text="status === 'loading' ? 'Loading...' : 'Send'"></button>
  </form>
</div>