nextjs-onsubmit

Next.js

Drop-in Next.js component and hook for onsubmit.dev. Works with both App Router and Pages Router — no backend required.

Installation

bash
npm install nextjs-onsubmit

Quick start

App Router

tsx
// app/contact/page.tsx
import { OnSubmitForm } from 'nextjs-onsubmit';

export default function ContactPage() {
  return (
    <OnSubmitForm
      formId="YOUR_FORM_ID"
      successMessage="Thanks! We'll be in touch."
    >
      <input name="name" placeholder="Your name" required />
      <input name="email" type="email" placeholder="Email" required />
      <textarea name="message" placeholder="Message" />
      <button type="submit">Send</button>
    </OnSubmitForm>
  );
}

Pages Router

tsx
// pages/contact.tsx
import { OnSubmitForm } from 'nextjs-onsubmit';

export default function ContactPage() {
  return (
    <OnSubmitForm
      formId="YOUR_FORM_ID"
      successMessage="Thanks! We'll be in touch."
    >
      <input name="name" placeholder="Your name" required />
      <input name="email" type="email" placeholder="Email" required />
      <button type="submit">Send</button>
    </OnSubmitForm>
  );
}
Your form ID is available in the onsubmit.dev dashboard after creating a form.

useOnSubmit hook

For full control over the form UI, use the hook directly:

tsx
'use client';
import { useOnSubmit } from 'nextjs-onsubmit';

export function ContactForm() {
  const { handleSubmit, isLoading, isSuccess, error, reset } = useOnSubmit({
    formId: 'YOUR_FORM_ID',
    onSuccess: (data) => console.log('Submitted!', data),
  });

  if (isSuccess) return <p>Thanks! We'll be in touch.</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Sending…' : 'Send'}
      </button>
      {error && <p>{error}</p>}
    </form>
  );
}

Router redirects

If the API response includes a redirect URL, pass router.push via onRedirect to navigate using the Next.js router instead of a hard redirect:

tsx
'use client';
import { useRouter } from 'next/navigation';
import { OnSubmitForm } from 'nextjs-onsubmit';

export function ContactForm() {
  const router = useRouter();

  return (
    <OnSubmitForm formId="YOUR_FORM_ID" onRedirect={router.push}>
      <input name="email" type="email" required />
      <button type="submit">Send</button>
    </OnSubmitForm>
  );
}

Component props

OnSubmitForm accepts the following props. All standard HTML <form> attributes are also accepted.

PropTypeDescription
formIdstringRequired. Your onsubmit.dev form ID.
successMessagestringText to show in place of the form on success.
endpointstringOverride the default API endpoint.
onSuccess(data) => voidCallback fired on successful submission.
onError(error) => voidCallback fired on submission error.
onRedirect(url: string) => voidCalled when the API returns a redirect URL.

Hook API

Options

OptionTypeDescription
formIdstringRequired. Your onsubmit.dev form ID.
endpointstringOverride the default API endpoint.
onSuccess(data) => voidCallback fired on successful submission.
onError(error) => voidCallback fired on submission error.
onRedirect(url: string) => voidCalled when the API returns a redirect URL.

Returns

ValueTypeDescription
handleSubmit(e) => Promise<void>Pass to the form's onSubmit.
isLoadingbooleantrue while the request is in flight.
isSuccessbooleantrue after a successful submission.
errorstring | nullError message, or null.
dataunknownRaw response data.
reset() => voidResets all state back to initial values.

File uploads

File inputs are handled automatically. When the form contains a file field the data is sent as multipart/form-data. Otherwise, JSON is used.