Skip to main content

Action Handler

Use Remix actions to forward form data to Spike:
app/routes/contact.tsx
import { ActionFunctionArgs, json } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";

export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData();
  
  const res = await fetch("https://spike.ac/api/f/YOUR_FORM_SLUG", {
    method: "POST",
    body: formData,
  });
  
  if (res.ok) {
    return json({ success: true });
  }
  
  return json({ success: false, error: "Failed to submit" });
}

export default function Contact() {
  const data = useActionData<typeof action>();
  
  return (
    <Form method="post">
      <input type="text" name="_gotcha" style={{ display: 'none' }} />
      
      <div>
        <label htmlFor="email">Email</label>
        <input type="email" id="email" name="email" required />
      </div>
      
      <div>
        <label htmlFor="message">Message</label>
        <textarea id="message" name="message" required />
      </div>
      
      <button type="submit">Send</button>
      
      {data?.success && <p>Thanks for your message!</p>}
      {data?.error && <p>{data.error}</p>}
    </Form>
  );
}

With Loading State

Add loading state using Remix’s useNavigation:
app/routes/contact.tsx
import { ActionFunctionArgs, json } from "@remix-run/node";
import { Form, useActionData, useNavigation } from "@remix-run/react";

export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData();
  
  const res = await fetch("https://spike.ac/api/f/YOUR_FORM_SLUG", {
    method: "POST",
    body: formData,
  });
  
  return json({ success: res.ok });
}

export default function Contact() {
  const data = useActionData<typeof action>();
  const navigation = useNavigation();
  const isSubmitting = navigation.state === "submitting";
  
  return (
    <Form method="post">
      <input type="email" name="email" required />
      <textarea name="message" required />
      
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? "Sending..." : "Send"}
      </button>
      
      {data?.success && <p>Thanks!</p>}
    </Form>
  );
}

Direct Submission

Or submit directly to Spike without a Remix action:
<form action="https://spike.ac/api/f/YOUR_FORM_SLUG" method="POST">
  <input type="hidden" name="_next" value="/thank-you" />
  <input type="text" name="_gotcha" style="display:none" />
  
  <input type="email" name="email" required />
  <button type="submit">Subscribe</button>
</form>