The open-source form backend where you bring your own API keys.
Website · Docs · Get started free · Self-host
OSForms is an open-source (MIT) form backend for HTML and React forms. Submissions are stored and sent to your own Resend email, Google Sheets, and webhooks using your own API keys. Because the integrations run on your keys, they are free on every tier: the hosted plan gives you 100 submissions a month with all of them included, and a self-hosted instance runs with limits you set.
- Create a free account and create a form. Copy its endpoint URL.
- Point your form at it.
<form action="https://osforms.com/api/v1/f/YOUR_FORM_SLUG" method="POST">
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message"></textarea>
<button type="submit">Send</button>
</form>The endpoint accepts application/x-www-form-urlencoded, multipart/form-data, and JSON. A classic HTML form post gets a 303 redirect to your thank-you page when you set a redirect URL on the form; fetch calls with a JSON body (or Accept: application/json) get { "success": true, "message": "Submission received" }. Field names are stored as-is, so no schema setup is needed.
React with @osforms/react
npm install @osforms/reactimport { OSForm } from '@osforms/react';
export default function ContactPage() {
// Renders the form you designed in the dashboard Builder, classic or conversational
return <OSForm formId="YOUR_FORM_SLUG" />;
}<OSForm /> can also take a schema prop (plus endpoint) if you'd rather define fields in code, a mode of classic, conversational, or stepped, and a baseUrl for self-hosted instances. See the SDK docs.
- Integrations are free because they run on your keys. Resend email (with auto-replies), Google Sheets, and webhooks are on the free tier. You pay your provider directly, if anything.
- You own the data. Submissions don't expire, export to CSV from any form, and a read-only pull API lets your own code fetch them.
- Open source, self-hostable. MIT licensed.
docker compose upruns the whole stack (Next.js + MongoDB) on your machine or server. - Spam protection without a paid plan. A honeypot field you name, per-form rate limits, and CAPTCHA on your own keys.
- A conversational React component.
@osforms/reactrenders Typeform-style one-question-at-a-time forms, or classic ones, from the same schema.
Free-tier facts from each vendor's pricing page or repository, checked Nov 2026. Prices drift, so follow the links.
| OSForms | Formspree | Web3Forms | Basin | Formgrid | |
|---|---|---|---|---|---|
| Open source | Yes (MIT) | No (repo archived July 2023) | No | No | Yes (MIT) |
| Self-host | Yes (Docker Compose) | No | No | No | Yes (Docker Compose) |
| Free tier | 100 submissions/mo | 50/mo, 30-day archive | 250/mo, email delivery | 50/mo, 1 form, 30-day retention | 50/mo, up to 3 forms |
| Google Sheets on free tier | Yes (your Google account) | No (from $15/mo Personal) | No (paid plans) | No (Growth plan) | One-time sync of 10 rows |
| Webhooks on free tier | Yes (optional HMAC signing) | No (from $30/mo Professional) | No (paid plans) | No (Growth plan) | No ($29/mo Business) |
Where others win: Web3Forms has more free volume for email-only forms, and Formspree, Basin, and Formgrid store file uploads on paid plans (OSForms doesn't store files at all). The full write-up, including Formcarry, Netlify Forms, and FormSubmit, is at osforms.com/formspree-alternative.
- Email via Resend: notification emails to you, plus an optional auto-reply to the submitter, sent with your own Resend API key.
- Google Sheets: appends a row per submission through your Google account. New form fields become new columns automatically, alongside
Submission IDandSubmitted Atcolumns. - Webhooks: POSTs each submission as JSON to any URL. With a signing secret, requests carry an
X-osforms-Signatureheader: the lowercase hex HMAC-SHA256 of the raw body (nosha256=prefix). - Integrations run in the background after the submission is stored, so the form gets an instant response. Failures are logged per submission and raise an in-app notification and an email alert.
- Honeypot: you choose the field name in form settings; submissions that fill it are silently dropped.
- Per-form rate limits (default 10 requests/min per IP).
- CAPTCHA on your own keys: Google reCAPTCHA, hCaptcha, or Cloudflare Turnstile.
- CORS allowed origins per form.
- Redirect to your own thank-you page after an HTML form post.
- CSV export of every form's submissions.
- Pull API:
GET /api/v1/submissions?formId=...with a read-onlyosf_live_API key, scoped to the forms you choose. Cursor-paginated, never returns IP or user-agent metadata. - Integration keys encrypted at rest with AES-256-GCM, decrypted only when an integration runs.
- Dashboard with submission analytics and per-submission integration logs.
Limitations: uploaded files are not stored. Multipart posts are accepted, but each file is replaced with a placeholder like [File: resume.pdf, 48210 bytes]. The hosted free tier is 100 submissions a month.
git clone https://github.com/hawkeye-sama/osforms.git
cd osforms
cp .env.docker.example .env # then generate the two secrets it lists
docker compose upThat builds the app, starts MongoDB, and serves OSForms at http://localhost:3000. Config lives in .env (secrets stay out of tracked files); MONGODB_URI defaults to the bundled mongo service. Every account still starts at 100 submissions a month, stored on the user record, so raise it in MongoDB after signup. The full walkthrough (generating secrets, your domain, integrations, the submission limit, and deploying to Vercel + MongoDB Atlas, which is what osforms.com runs on) is in SELF_HOSTING.md.
OSForms runs anywhere Next.js runs. On a long-running server (Docker, a VPS) background integrations run in-process with no time cap. On Vercel they use waitUntil, capped at ~10s on Hobby and up to 60s on Pro.
Forks ship with a scheduled GitHub Action (.github/workflows/sync-fork.yml) that keeps your fork's main up to date with upstream automatically. It only runs on forks, never on the upstream repo. Enable Actions on your fork once and it runs daily. See SELF_HOSTING.md → Keep your fork in sync.
OSForms is a pnpm + Turborepo monorepo:
.
├── apps/
│ ├── web/ # The Next.js 15 app: dashboard, API, public submission endpoint
│ ├── docs/ # Mintlify documentation site (osforms.com/docs)
│ └── playground/ # Vite sandbox for trying @osforms/react against your local app
├── packages/
│ ├── react/ # @osforms/react, the published React SDK
│ └── types/ # @osforms/types, shared TypeScript types
├── docker-compose.yml # Docker self-host: MongoDB + the web app
└── turbo.json
The app source lives in apps/web/src. The environment file lives at apps/web/.env.example.
- Node.js 20+
- pnpm 10+:
corepack enable(ships with Node) ornpm i -g pnpm - Docker (for local MongoDB) or a MongoDB connection string
# 1. Clone (or your fork)
git clone https://github.com/hawkeye-sama/osforms.git
cd osforms
# 2. Install all workspace dependencies
pnpm install
# 3. Create your env file (note the path: it's inside apps/web)
cp apps/web/.env.example apps/web/.envEdit apps/web/.env. At minimum you need:
MONGODB_URI=mongodb://localhost:27017/osforms
JWT_SECRET=any-long-random-string
ENCRYPTION_KEY=<64-char hex string> # node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
NEXT_PUBLIC_APP_URL=http://localhost:3000Google and Resend keys are optional. See Environment variables.
# Start only MongoDB (plain `docker compose up` also builds the web app on port 3000)
docker compose up -d mongodb
# Build the workspace packages once (the web app imports @osforms/react)
pnpm build --filter @osforms/react
# Start the dev server
pnpm devOpen http://localhost:3000, create an account, and complete onboarding.
pnpm devruns Turborepo, which starts the Next.js app, rebuilds@osforms/reactin watch mode, and serves the SDK playground at http://localhost:3001. Building the package once first avoids a first-run race where the app boots before the SDK is built.
All variables live in apps/web/.env (copy from apps/web/.env.example).
| Variable | Description | Required |
|---|---|---|
MONGODB_URI |
MongoDB connection string | Yes |
JWT_SECRET |
Secret for signing JWT auth tokens | Yes |
ENCRYPTION_KEY |
64-char hex string (32 bytes) for AES-256-GCM | Yes |
NEXT_PUBLIC_APP_URL |
Public URL: OAuth callback, endpoint URLs, email links, sitemap | Yes |
GOOGLE_CLIENT_ID |
Google OAuth client ID (Google Sheets only) | No |
GOOGLE_CLIENT_SECRET |
Google OAuth client secret | No |
RESEND_API_KEY |
System emails only (OTP, welcome, failure alerts) | No |
RESEND_EMAIL_SIGNING_SECRET |
osforms.com's inbound forwarding only; leave blank | No |
NEXT_PUBLIC_RECAPTCHA_SITE_KEY |
reCAPTCHA v3 site key for signup spam protection | No |
RECAPTCHA_SECRET_KEY |
reCAPTCHA v3 secret; turns the signup CAPTCHA on | No |
RECAPTCHA_MIN_SCORE |
Minimum reCAPTCHA score to allow signup (default 0.5) | No |
RESEND_API_KEYis not used by the Resend email integration; each integration stores its own key.System emails are sent from hardcoded
@osforms.comaddresses, so a self-hosted instance can't deliver them yet, even with your own Resend key (and without one, signup returns a 500 after saving your account). The OTP is never logged. To verify, read the latest code from MongoDB and enter it at/verify-email?email=<your email>. See SELF_HOSTING.md → Verify your account without email.
Running a public instance? Turn on the optional signup CAPTCHA to block bot accounts. See Signup spam protection.
Scripts run from the repo root via Turborepo (they fan out to every workspace package):
pnpm dev # Start the app, SDK watch build, and playground
pnpm build # Production build of all packages
pnpm lint # ESLint with --fix (rewrites files); typechecks the SDK packages, not the web app
pnpm turbo lint:check # Lint without fixing + typecheck web, react, and types
pnpm format # Format with Prettier
pnpm format:check # Check formattingTarget a single package with --filter, e.g. pnpm build --filter @osforms/web.
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router) |
| Monorepo | pnpm workspaces + Turborepo |
| Database | MongoDB + Mongoose |
| Auth | Email/password + OTP verification + JWT |
| Encryption | AES-256-GCM (Node crypto) |
| Styling | Tailwind CSS + shadcn/ui |
| Integrations | Resend, Google Sheets (googleapis), Webhooks |
| Background jobs | @vercel/functions waitUntil |
| Charts | Recharts |
| SDK | @osforms/react (built with tsup) |
OSForms (osforms.com, @osforms/react) is unrelated to OS2Forms, Open Forms, and OpnForm, which are separate projects.
Contributions are welcome. See CONTRIBUTING.md for environment setup, code style, and how to open a pull request. Found a bug? Open an issue.
MIT. See LICENSE.
If OSForms saves you a monthly bill, a ⭐ helps other developers find it.
