Client Component
For client-side form handling, use the React library:Copy
'use client';
import { useForm } from '@spike-forms/react';
export function ContactForm() {
const { state, handleSubmit, reset } = useForm('YOUR_FORM_SLUG');
if (state.succeeded) {
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={state.submitting}>
{state.submitting ? 'Sending...' : 'Send'}
</button>
</form>
);
}
Server Action
Use Next.js Server Actions for server-side submission:Copy
// app/contact/page.tsx
import { submitForm } from './actions';
export default function ContactPage() {
return (
<form action={submitForm}>
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<button type="submit">Send</button>
</form>
);
}
Copy
// app/contact/actions.ts
'use server';
import { redirect } from 'next/navigation';
export async function submitForm(formData: FormData) {
const response = await fetch('https://spike.ac/api/f/YOUR_FORM_SLUG', {
method: 'POST',
body: formData,
});
if (response.ok) {
redirect('/thanks');
}
throw new Error('Form submission failed');
}
With useFormState
For better UX with Server Actions:Copy
'use client';
import { useFormState, useFormStatus } from 'react-dom';
import { submitForm } from './actions';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Sending...' : 'Send'}
</button>
);
}
export function ContactForm() {
const [state, formAction] = useFormState(submitForm, { success: false });
if (state.success) {
return <p>Thanks for your message!</p>;
}
return (
<form action={formAction}>
<input type="email" name="email" required />
<textarea name="message" required />
<SubmitButton />
{state.error && <p className="error">{state.error}</p>}
</form>
);
}
Copy
// actions.ts
'use server';
export async function submitForm(prevState: any, formData: FormData) {
const response = await fetch('https://spike.ac/api/f/YOUR_FORM_SLUG', {
method: 'POST',
body: formData,
headers: { 'Accept': 'application/json' }
});
if (response.ok) {
return { success: true };
}
const data = await response.json();
return { success: false, error: data.error || 'Submission failed' };
}
API Route Proxy
If you need to add custom logic, create an API route:Copy
// app/api/contact/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const formData = await request.formData();
// Add custom validation or processing here
const email = formData.get('email');
if (!email || !email.toString().includes('@')) {
return NextResponse.json({ error: 'Invalid email' }, { status: 400 });
}
// Forward to Spike
const response = await fetch('https://spike.ac/api/f/YOUR_FORM_SLUG', {
method: 'POST',
body: formData,
headers: { 'Accept': 'application/json' }
});
const data = await response.json();
return NextResponse.json(data, { status: response.status });
}
Environment Variables
Store your form slug in environment variables:Copy
NEXT_PUBLIC_SPIKE_FORM_SLUG=abc123xyz
Copy
const { handleSubmit } = useForm(process.env.NEXT_PUBLIC_SPIKE_FORM_SLUG!);