Skip to main content

Installation

Install the official React library:
npm install @spike-forms/react

Using the Hook

The useForm hook provides everything you need:
import { useForm } from '@spike-forms/react';

function ContactForm() {
  const { state, handleSubmit, reset } = useForm('YOUR_FORM_SLUG');

  if (state.succeeded) {
    return (
      <div>
        <p>Thanks for your message!</p>
        <button onClick={reset}>Send another</button>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" name="email" placeholder="Email" required />
      <textarea name="message" placeholder="Message" required />
      <button type="submit" disabled={state.submitting}>
        {state.submitting ? 'Sending...' : 'Send'}
      </button>
      {state.errors && <p className="error">{state.errors}</p>}
    </form>
  );
}

Using the Component

For simpler cases, use the SpikeForm component:
import { SpikeForm, ValidationError } from '@spike-forms/react';

function ContactForm() {
  return (
    <SpikeForm formSlug="YOUR_FORM_SLUG">
      <input type="email" name="email" placeholder="Email" required />
      <ValidationError field="email" />
      
      <textarea name="message" placeholder="Message" required />
      <ValidationError field="message" />
      
      <button type="submit">Send</button>
    </SpikeForm>
  );
}

Without the Library

You can also use Spike with plain fetch:
import { useState } from 'react';

function ContactForm() {
  const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus('loading');

    const form = e.currentTarget;
    const data = new FormData(form);

    try {
      const response = await fetch('https://spike.ac/api/f/YOUR_FORM_SLUG', {
        method: 'POST',
        body: data,
        headers: { 'Accept': 'application/json' }
      });

      if (response.ok) {
        setStatus('success');
        form.reset();
      } else {
        setStatus('error');
      }
    } catch {
      setStatus('error');
    }
  }

  if (status === 'success') {
    return <p>Thanks for your message!</p>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" name="email" placeholder="Email" required />
      <textarea name="message" placeholder="Message" required />
      <button type="submit" disabled={status === 'loading'}>
        {status === 'loading' ? 'Sending...' : 'Send'}
      </button>
      {status === 'error' && <p>Something went wrong.</p>}
    </form>
  );
}

With React Hook Form

import { useForm } from 'react-hook-form';

type FormData = {
  email: string;
  message: string;
};

function ContactForm() {
  const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>();

  async function onSubmit(data: FormData) {
    const response = await fetch('https://spike.ac/api/f/YOUR_FORM_SLUG', {
      method: 'POST',
      body: JSON.stringify(data),
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      }
    });
    
    if (response.ok) {
      alert('Message sent!');
    }
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="email"
        {...register('email', { required: 'Email is required' })}
      />
      {errors.email && <span>{errors.email.message}</span>}
      
      <textarea
        {...register('message', { required: 'Message is required' })}
      />
      {errors.message && <span>{errors.message.message}</span>}
      
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Sending...' : 'Send'}
      </button>
    </form>
  );
}

TypeScript Support

The library is fully typed:
import { useForm, FormState } from '@spike-forms/react';

interface ContactFormData {
  email: string;
  message: string;
  phone?: string;
}

function ContactForm() {
  const { state, handleSubmit } = useForm<ContactFormData>('YOUR_FORM_SLUG');
  // state is typed as FormState<ContactFormData>
}