npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jitforms/react

v0.1.0

Published

React hook (useJitForm) for JitForms — form state, validation errors, loading states.

Readme

@jitforms/react

React hooks for JitForms - the simplest way to add form handling to your React app.

Installation

npm install @jitforms/react

Basic Usage

import { useJitForm } from '@jitforms/react';

function ContactForm() {
  const { submit, isLoading, isSuccess, fieldError } = useJitForm('your-form-hash');

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    await submit(Object.fromEntries(formData));
  };

  if (isSuccess) return <p>Thank you for your message!</p>;

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">Name</label>
        <input id="name" name="name" />
        {fieldError('name') && <span className="error">{fieldError('name')}</span>}
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <input id="email" name="email" type="email" />
        {fieldError('email') && <span className="error">{fieldError('email')}</span>}
      </div>

      <div>
        <label htmlFor="message">Message</label>
        <textarea id="message" name="message" />
        {fieldError('message') && <span className="error">{fieldError('message')}</span>}
      </div>

      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Sending...' : 'Send'}
      </button>
    </form>
  );
}

With Validation Errors

Display all validation errors at once, or per-field:

import { useJitForm } from '@jitforms/react';

function SignupForm() {
  const { submit, isLoading, isError, error, fieldErrors, fieldError } =
    useJitForm('your-form-hash');

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    await submit(Object.fromEntries(formData));
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* Show a global error message */}
      {isError && <div className="alert">{error?.message}</div>}

      {/* Show all field errors as a summary */}
      {isError && (
        <ul>
          {Object.entries(fieldErrors).map(([field, messages]) =>
            messages.map((msg, i) => (
              <li key={`${field}-${i}`}>{msg}</li>
            ))
          )}
        </ul>
      )}

      <div>
        <input name="email" type="email" placeholder="Email" />
        {fieldError('email') && <span className="error">{fieldError('email')}</span>}
      </div>

      <div>
        <input name="password" type="password" placeholder="Password" />
        {fieldError('password') && <span className="error">{fieldError('password')}</span>}
      </div>

      <button type="submit" disabled={isLoading}>Sign Up</button>
    </form>
  );
}

With Captcha

Pass a captcha token (e.g. from reCAPTCHA or Turnstile) via the options:

import { useJitForm } from '@jitforms/react';
import { useState } from 'react';

function ProtectedForm() {
  const [captchaToken, setCaptchaToken] = useState('');

  const { submit, isLoading, isSuccess, fieldError } = useJitForm('your-form-hash', {
    captchaToken,
  });

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    await submit(Object.fromEntries(formData));
  };

  if (isSuccess) return <p>Submitted successfully!</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" placeholder="Email" />
      {fieldError('email') && <span className="error">{fieldError('email')}</span>}

      {/* Your captcha component */}
      <TurnstileWidget onVerify={setCaptchaToken} />

      <button type="submit" disabled={isLoading || !captchaToken}>Submit</button>
    </form>
  );
}

With File Uploads

Files are supported directly via the JitFormData type:

import { useJitForm } from '@jitforms/react';
import { useRef } from 'react';

function UploadForm() {
  const fileRef = useRef<HTMLInputElement>(null);
  const { submit, isLoading, isSuccess, fieldError } = useJitForm('your-form-hash');

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    await submit(Object.fromEntries(formData));
  };

  if (isSuccess) return <p>File uploaded!</p>;

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">Name</label>
        <input id="name" name="name" />
        {fieldError('name') && <span className="error">{fieldError('name')}</span>}
      </div>

      <div>
        <label htmlFor="resume">Resume (PDF)</label>
        <input id="resume" name="resume" type="file" accept=".pdf" ref={fileRef} />
        {fieldError('resume') && <span className="error">{fieldError('resume')}</span>}
      </div>

      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Uploading...' : 'Upload'}
      </button>
    </form>
  );
}

Reset After Submission

Use reset() to allow the user to submit another response:

import { useJitForm } from '@jitforms/react';

function FeedbackForm() {
  const { submit, isLoading, isSuccess, reset, fieldError } = useJitForm('your-form-hash');

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    await submit(Object.fromEntries(formData));
  };

  if (isSuccess) {
    return (
      <div>
        <p>Thanks for your feedback!</p>
        <button onClick={reset}>Submit another response</button>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="feedback">Your feedback</label>
        <textarea id="feedback" name="feedback" />
        {fieldError('feedback') && <span className="error">{fieldError('feedback')}</span>}
      </div>

      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Sending...' : 'Send Feedback'}
      </button>
    </form>
  );
}

API Reference

useJitForm(formHash, options?)

Returns a form handler bound to the given form hash.

Parameters:

| Parameter | Type | Description | |---|---|---| | formHash | string | Your JitForms form identifier | | options.baseUrl | string | Custom API base URL (default: 'https://jitforms.com') | | options.honeypot | boolean \| string | Enable honeypot spam protection | | options.captchaToken | string | Captcha verification token | | options.headers | Record<string, string> | Custom request headers | | options.timeout | number | Request timeout in ms (default: 10000) |

Returns:

| Property | Type | Description | |---|---|---| | submit | (data: JitFormData) => Promise<JitFormResult> | Submit form data | | isLoading | boolean | true while a submission is in-flight | | isSuccess | boolean | true after a successful submission | | isError | boolean | true after a failed submission | | error | { message: string; errors: Record<string, string[]> } \| null | Error details | | submission | JitFormSubmission \| null | The created submission on success | | redirectUrl | string \| undefined | Redirect URL returned by the server | | reset | () => void | Reset all state back to initial | | fieldErrors | Record<string, string[]> | Shortcut to error.errors (empty object if no error) | | fieldError | (field: string) => string \| undefined | Returns the first error message for a field |

License

MIT