Examples

Framework Integrations

Copy-paste examples to connect Formtorch to Next.js, React, Vue, Astro, and plain HTML.

Replace https://formtorch.com/f/abc123 with your real endpoint from the Formtorch dashboard.

HTML

No-framework

The simplest integration — point a standard HTML form's action attribute at your Formtorch endpoint. No JavaScript required for basic submissions.

A plain HTML form that submits via a full-page POST.

html
<form
  action="https://formtorch.com/f/abc123"
  method="POST"
>
  <input type="text" name="name" placeholder="Name" required />
  <input type="email" name="email" placeholder="Email" required />
  <textarea name="message" placeholder="Message"></textarea>
  <button type="submit">Send message</button>
</form>

Next.js

App Router

Three patterns for Next.js App Router: a client component with fetch, a server action that runs on the server, and a lightweight API route handler.

A 'use client' form component that posts to Formtorch via fetch.

tsx
'use client';
import { useState } from 'react';

export default function ContactForm() {
  const [status, setStatus] = useState<
    'idle' | 'sending' | 'done'
  >('idle');

  async function handleSubmit(
    e: React.FormEvent<HTMLFormElement>
  ) {
    e.preventDefault();
    setStatus('sending');
    const res = await fetch('https://formtorch.com/f/abc123', {
      method: 'POST',
      headers: { 'X-Requested-With': 'XMLHttpRequest' },
      body: new FormData(e.currentTarget),
    });
    setStatus(res.ok ? 'done' : 'idle');
  }

  if (status === 'done') {
    return <p>Thanks! We will be in touch.</p>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" required />
      <input name="email" type="email" placeholder="Email" required />
      <textarea name="message" placeholder="Message" />
      <button type="submit" disabled={status === 'sending'}>
        {status === 'sending' ? 'Sending...' : 'Send message'}
      </button>
    </form>
  );
}

React

Client-side

Two approaches for React: controlled inputs with a JSON payload, and uncontrolled inputs using the native FormData API.

Manage input state with useState and POST as JSON.

tsx
import { useState } from 'react';

type Fields = { name: string; email: string; message: string };

export function ContactForm() {
  const [fields, setFields] = useState<Fields>({
    name: '', email: '', message: '',
  });
  const [sent, setSent] = useState(false);

  function set(
    e: React.ChangeEvent<
      HTMLInputElement | HTMLTextAreaElement
    >
  ) {
    setFields((f) => ({ ...f, [e.target.name]: e.target.value }));
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    await fetch('https://formtorch.com/f/abc123', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
      },
      body: JSON.stringify(fields),
    });
    setSent(true);
  }

  if (sent) return <p>Thanks! We will be in touch.</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" value={fields.name} onChange={set} required />
      <input
        name="email"
        type="email"
        value={fields.email}
        onChange={set}
        required
      />
      <textarea
        name="message"
        value={fields.message}
        onChange={set}
      />
      <button type="submit">Send</button>
    </form>
  );
}

Astro

Static site

Astro renders to static HTML, so a plain form with an action attribute works out of the box — no JavaScript island required.

Drop a standard HTML form into any .astro file.

astro
---
// src/pages/contact.astro
---

<form
  action="https://formtorch.com/f/abc123"
  method="POST"
>
  <input type="text" name="name" placeholder="Name" required />
  <input type="email" name="email" placeholder="Email" required />
  <textarea name="message" placeholder="Message"></textarea>
  <button type="submit">Send message</button>
</form>
Because Astro generates static HTML, no client-side JS is needed for a basic POST. For AJAX submission wrap the form in a client:load island.

Vue

Client-side

A single-file component using v-model for reactive input binding and @submit.prevent for AJAX submission.

v-model bindings with a Fetch POST on submit.

vue
<template>
  <div v-if="sent">
    <p>Thanks! Message sent.</p>
  </div>
  <form v-else @submit.prevent="handleSubmit">
    <input
      v-model="name"
      name="name"
      placeholder="Name"
      required
    />
    <input
      v-model="email"
      type="email"
      name="email"
      placeholder="Email"
      required
    />
    <textarea
      v-model="message"
      name="message"
      placeholder="Message"
    ></textarea>
    <button type="submit" :disabled="sending">
      {{ sending ? 'Sending...' : 'Send message' }}
    </button>
  </form>
</template>

<script setup>
import { ref } from 'vue';

const name = ref('');
const email = ref('');
const message = ref('');
const sending = ref(false);
const sent = ref(false);

async function handleSubmit() {
  sending.value = true;
  await fetch('https://formtorch.com/f/abc123', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name: name.value,
      email: email.value,
      message: message.value,
    }),
  });
  sent.value = true;
}
</script>

SvelteKit

Client-side

Use Svelte reactive variables with bind:value and on:submit for a clean, minimal integration.

bind:value reactive inputs with on:submit|preventDefault.

svelte
<script lang="ts">
  let name = '';
  let email = '';
  let message = '';
  let sent = false;

  async function handleSubmit() {
    await fetch('https://formtorch.com/f/abc123', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, email, message }),
    });
    sent = true;
  }
</script>

{#if sent}
  <p>Thanks! Message sent.</p>
{:else}
  <form on:submit|preventDefault={handleSubmit}>
    <input
      bind:value={name}
      name="name"
      placeholder="Name"
      required
    />
    <input
      bind:value={email}
      type="email"
      name="email"
      placeholder="Email"
      required
    />
    <textarea
      bind:value={message}
      name="message"
      placeholder="Message"
    ></textarea>
    <button type="submit">Send message</button>
  </form>
{/if}

Gatsby

Client-side

Gatsby is built on React, so the same hooks-based pattern applies. Submit with FormData for the simplest integration.

A standard React form component using FormData and fetch.

jsx
import React, { useState } from 'react';

export default function ContactForm() {
  const [sent, setSent] = useState(false);

  async function handleSubmit(e) {
    e.preventDefault();
    await fetch('https://formtorch.com/f/abc123', {
      method: 'POST',
      headers: { 'X-Requested-With': 'XMLHttpRequest' },
      body: new FormData(e.target),
    });
    setSent(true);
  }

  if (sent) return <p>Thanks! Message sent.</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" required />
      <input
        name="email"
        type="email"
        placeholder="Email"
        required
      />
      <textarea name="message" placeholder="Message" />
      <button type="submit">Send message</button>
    </form>
  );
}

Webflow

No-code

Add a Formtorch-powered form to any Webflow project using an HTML Embed element. No custom code editor access required.

Paste this into a Webflow HTML Embed block on any page.

html
<!-- Paste into a Webflow HTML Embed element -->
<form
  action="https://formtorch.com/f/abc123"
  method="POST"
>
  <input type="text" name="name" placeholder="Name" required />
  <input type="email" name="email" placeholder="Email" required />
  <textarea name="message" placeholder="Message"></textarea>
  <button type="submit">Send message</button>
</form>
In Webflow, drag an HTML Embed element onto the canvas, open its settings, and paste the code above. Style inputs using Webflow's Designer or custom CSS.

Ship forms without maintaining a backend

Create an endpoint, paste it into your form, and start receiving submissions.