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

@formflow.sh/react

v0.2.0

Published

React form hook with built-in backend submission. No server code needed. Works with shadcn/ui, MUI, and any component library. Built on react-hook-form.

Readme

@formflow.sh/react

Production-grade React form hook with built-in backend submission. No server code needed.

Installation

npm install @formflow.sh/react
# or
pnpm add @formflow.sh/react
# or
yarn add @formflow.sh/react

Quick Start

import { useFormFlow } from '@formflow.sh/react';
import { Input } from '@/components/ui/input'; // any UI library
import { Button } from '@/components/ui/button';

export function ContactForm() {
  const { register, handleSubmit, formState } = useFormFlow({
    apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY,
    onSuccess: () => alert('Thanks for your message!'),
  });

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <Input {...register('email')} type="email" required />
      <Input {...register('name')} required />
      <Button type="submit" disabled={formState.isSubmitting}>
        {formState.isSubmitting ? 'Sending...' : 'Submit'}
      </Button>
    </form>
  );
}

That's it! Your form submits to FormFlow's backend automatically. No API routes, no server code.

Why useFormFlow?

  • Works with ANY UI library: shadcn/ui, Material-UI, Chakra UI, or native HTML
  • Built on react-hook-form: Battle-tested validation and state management (41k stars)
  • Backend included: Form submission, storage, and email notifications handled automatically
  • TypeScript-first: Excellent IntelliSense and type safety
  • Production-ready: Comprehensive test coverage, industry-standard patterns

API Reference

useFormFlow(options)

React hook for form state management and backend submission.

Options

All options from react-hook-form are supported, plus:

  • apiKey (string, required): FormFlow API key from dashboard

    apiKey: 'pk_live_abc123'
  • onSuccess (function, optional): Called after successful submission

    onSuccess: (data, response) => {
      console.log('Submitted:', data);
      console.log('Submission ID:', response.submissionId);
    }
  • onError (function, optional): Called when submission fails

    onError: (error) => {
      console.error('Error:', error.message);
    }
  • defaultValues (object, optional): Initial form values

    defaultValues: {
      email: '[email protected]',
      newsletter: true
    }

Returns

All methods from react-hook-form are available, including:

  • register(name, options): Register form field

    <input {...register('email', { required: true })} />
  • handleSubmit: Form submission handler (enhanced to submit to FormFlow)

    <form onSubmit={handleSubmit}>...</form>
  • formState: Current form state

    formState.isSubmitting // true while submitting
    formState.errors // validation errors
    formState.isValid // form validity
  • setValue(name, value): Programmatically set field value

    setValue('email', '[email protected]')
  • getValues(): Get all current values

    const values = getValues()
  • reset(): Reset form to defaults

    reset()
  • watch(name): Watch field value for conditional logic

    const newsletter = watch('newsletter')

Works With Any UI Library

shadcn/ui

import { useFormFlow } from '@formflow.sh/react';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';

const { register, handleSubmit, formState } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY
});

<div>
  <Label htmlFor="email">Email</Label>
  <Input {...register('email')} id="email" type="email" required />
</div>

Material-UI

import { useFormFlow } from '@formflow.sh/react';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';

const { register, handleSubmit } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY
});

<TextField {...register('email')} type="email" label="Email" required />
<Button type="submit">Submit</Button>

Chakra UI

import { useFormFlow } from '@formflow.sh/react';
import { Input, Button } from '@chakra-ui/react';

const { register, handleSubmit } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY
});

<Input {...register('email')} type="email" placeholder="Email" />
<Button type="submit">Submit</Button>

Native HTML

import { useFormFlow } from '@formflow.sh/react';

const { register, handleSubmit, formState } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY
});

<form onSubmit={handleSubmit}>
  <input {...register('email')} type="email" required />
  <button type="submit" disabled={formState.isSubmitting}>
    Submit
  </button>
</form>

Form Validation

FormFlow uses react-hook-form's validation, which is both powerful and flexible.

Basic Validation

<input
  {...register('email', {
    required: 'Email is required',
    pattern: {
      value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
      message: 'Invalid email address'
    }
  })}
  type="email"
/>
{formState.errors.email && (
  <span className="error">{formState.errors.email.message}</span>
)}

Advanced Validation

<input
  {...register('password', {
    required: 'Password is required',
    minLength: {
      value: 8,
      message: 'Password must be at least 8 characters'
    },
    validate: {
      hasUpperCase: (value) =>
        /[A-Z]/.test(value) || 'Password must contain an uppercase letter',
      hasLowerCase: (value) =>
        /[a-z]/.test(value) || 'Password must contain a lowercase letter',
      hasNumber: (value) =>
        /\d/.test(value) || 'Password must contain a number'
    }
  })}
  type="password"
/>

Common Validation Rules

  • required: Field is required
  • pattern: Regex pattern matching
  • minLength / maxLength: String length validation
  • min / max: Number range validation
  • validate: Custom validation functions

See react-hook-form validation docs for all options.

Examples

See the /examples directory for complete working examples:

1. Basic Form

Simplest possible implementation using native HTML elements.

Features: Native HTML inputs, basic error handling, loading states

When to use: Quick prototypes, simple forms, learning the basics

2. shadcn/ui Integration

Shows integration with shadcn/ui component library.

Features: shadcn/ui Input, Button, Label, Textarea components

When to use: Production apps using shadcn/ui, modern design systems

3. Form Validation

Comprehensive validation patterns using react-hook-form.

Features: Required fields, email patterns, min/max length, number ranges, custom validation

When to use: Forms requiring data validation, user input safety

4. Advanced Features

Advanced patterns for production applications.

Features: Default values, error handling, controlled components with watch(), conditional fields, form reset

When to use: Complex forms, production applications, advanced requirements

Live Examples

Visit formflow.sh/docs/examples for interactive demos you can test in your browser.

Common Patterns

Newsletter Signup

const { register, handleSubmit, formState } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY,
  onSuccess: () => alert('Subscribed!'),
});

return (
  <form onSubmit={handleSubmit} className="flex gap-2">
    <input {...register('email')} type="email" required />
    <button type="submit" disabled={formState.isSubmitting}>
      Subscribe
    </button>
  </form>
);

Contact Form with Toast

import { toast } from 'sonner'; // or your toast library

const { register, handleSubmit } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY,
  onSuccess: () => toast.success('Message sent!'),
  onError: () => toast.error('Failed to send message'),
});

Multi-Step Form

const [step, setStep] = useState(1);
const { register, handleSubmit, trigger } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY,
});

const handleNext = async () => {
  const isValid = await trigger(['email', 'name']); // Validate current step
  if (isValid) setStep(2);
};

Error Handling

FormFlow provides detailed error information for different failure scenarios:

const { register, handleSubmit } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY,
  onError: (error) => {
    if (error.message?.includes('401')) {
      alert('Invalid API key');
    } else if (error.message?.includes('402')) {
      alert('Submission quota exceeded. Please upgrade your plan.');
    } else if (error.message?.includes('429')) {
      alert('Too many requests. Please try again in a few minutes.');
    } else if (error.message?.includes('Network')) {
      alert('Network error. Please check your internet connection.');
    } else {
      alert('An unexpected error occurred. Please try again.');
    }
  },
});

Development Mode

All forms work without an API key during development. Submissions are logged to the console:

const { register, handleSubmit } = useFormFlow({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY, // undefined in dev = console logs
});

To test with a real backend:

  1. Sign up at formflow.sh
  2. Get your API key from the dashboard
  3. Add it to your .env file:
    NEXT_PUBLIC_FORMFLOW_API_KEY=pk_live_xxx

Environment Variables

Next.js

NEXT_PUBLIC_FORMFLOW_API_KEY=your_api_key_here

Vite

VITE_FORMFLOW_API_KEY=your_api_key_here

Update your code:

apiKey: import.meta.env.VITE_FORMFLOW_API_KEY

Create React App

REACT_APP_FORMFLOW_API_KEY=your_api_key_here

Update your code:

apiKey: process.env.REACT_APP_FORMFLOW_API_KEY

TypeScript

Fully typed with excellent IntelliSense support:

import type {
  UseFormFlowOptions,
  UseFormFlowReturn,
  FormFlowSubmitResponse,
  FormFlowError
} from '@formflow.sh/react';

// Type your form data
interface ContactFormData {
  email: string;
  name: string;
  message: string;
}

const { register, handleSubmit } = useFormFlow<ContactFormData>({
  apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY,
  onSuccess: (data, response) => {
    // data is typed as ContactFormData
    console.log(data.email);
    console.log(response.submissionId);
  },
});

Migration from Wrapper Components

If you're using the old wrapper-based components (<FormFlow>, <ContactForm>), they still work but are deprecated. Migrate to the hook-based API:

Before:

import { ContactForm } from '@formflow.sh/react';

<ContactForm apiKey="pk_live_xxx" theme="minimal" />

After:

import { useFormFlow } from '@formflow.sh/react';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';

export function ContactForm() {
  const { register, handleSubmit, formState } = useFormFlow({
    apiKey: process.env.NEXT_PUBLIC_FORMFLOW_API_KEY,
  });

  return (
    <form onSubmit={handleSubmit}>
      <Input {...register('email')} type="email" required />
      <Input {...register('name')} required />
      <Button type="submit" disabled={formState.isSubmitting}>
        Submit
      </Button>
    </form>
  );
}

Benefits:

  • Works with your existing component library
  • More flexible and customizable
  • Industry-standard pattern
  • Better TypeScript support

Troubleshooting

"Cannot find module '@formflow.sh/react'"

Make sure you've installed the package:

npm install @formflow.sh/react

"API key invalid" error

Check that your API key is correctly set in your .env file and starts with pk_live_ or pk_test_.

Form not submitting

  1. Check browser console for errors
  2. Verify handleSubmit is passed to onSubmit prop
  3. Ensure form has a submit button with type="submit"
  4. Confirm API key is set (or remove it for dev mode)

TypeScript errors

Ensure you have the latest version:

npm install @formflow.sh/react@latest

Validation not working

react-hook-form validation is client-side only. Make sure to:

  1. Pass validation options to register()
  2. Display errors from formState.errors
  3. Check formState.isValid before allowing submission

Contributing

Found a useful pattern? Submit a PR with a new example in the /examples directory!

Learn More

Pricing

  • Free: 50 submissions/month
  • Maker: $9/month - 1,000 submissions
  • Pro: $29/month - 10,000 submissions
  • Business: $99/month - 100,000 submissions

See formflow.sh/pricing for details.

License

MIT