@formgong/core

Tiny, typed, zero-dependency client for Formgong, the form backend for contact forms: submissions go to email, Telegram and webhooks. Browser, Node, Deno, Bun and edge. A Formspree / Web3Forms alternative.
232
Install
npm install @formgong/core
Latest Version:0.1.0
License:MIT
Last Updated:Oct 3, 2026
Links: GitHub  ·  npm
Maintainer: formgong

@formgong/core

npm size types CI license

A tiny, typed client for Formgong, the form backend for contact forms. Post a form from any JavaScript runtime and the submission arrives by email, Telegram or webhook. You don't need a server, a database or SMTP.

  • About 3 kB min+gzip, no dependencies. ESM and CJS builds with TypeScript types.
  • Runs anywhere: browsers, Node 18+, Deno, Bun, Cloudflare Workers, Vercel and Netlify Edge.
  • Typed errors carry stable codes such as unknown_access_key, rate_limited and turnstile_missing.
  • Anti-spam built in: honeypot handling, and the same behaviour signals and proof-of-work as Formgong's fg.js.
  • Account API (@formgong/core/api) for scripts and AI agents: list forms, create a form, get a snippet, read submissions.
npm install @formgong/core

Quick start

import { submit, FormgongError } from "@formgong/core";

try {
  const { id, message } = await submit("fk_your_access_key", {
    name: "Ada Lovelace",
    email: "ada@example.com",
    message: "Hello!",
  });
  console.log(message); // "Sent. Thank you!" (localized)
} catch (error) {
  if (error instanceof FormgongError) console.error(error.code, error.message);
}

Get a free access key (fk_…) at formgong.com. It is public by design: it can only send submissions to your form, so it is safe in frontend code.

Browser forms

import { createTracker, submit } from "@formgong/core";

const form = document.querySelector("form")!;
// Optional: the same anti-spam signals as fg.js (typing/paste counts + a small proof-of-work).
const tracker = createTracker(form, "fk_your_access_key");

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const result = await submit("fk_your_access_key", form, { tracker, lang: "de" });
  form.reset();
  tracker.next();
  alert(result.message);
});

Add a hidden honeypot field to the form, <input name="botcheck" tabindex="-1" autocomplete="off" style="position:absolute;left:-10000px">, and keep it empty.

API

submit(accessKey, data, options?) => Promise<SubmitResult>

data can be a plain object, FormData, URLSearchParams or an HTMLFormElement. Objects are sent as JSON, with arrays joined as "a, b". Objects that contain a Blob or File are sent as multipart. FormData is copied, so your object is never modified.

Option Description
lang Visitor language for messages and the autoreply: en uk pl tr de es fr pt ar he hi ja. Defaults to <html lang> in browsers.
subject, replyTo, fromName Email subject, Reply-To address and sender name (_subject, _replyto, _from_name).
turnstileToken Cloudflare Turnstile token, if the form has Turnstile enabled.
tracker A createTracker() instance. It adds _fg_b and _fg_pow, exactly like fg.js.
honeypot "send" (default) lets Formgong file a filled honeypot as spam. "drop" resolves locally without sending a request.
origin Server-side only: the Origin header to send, for forms restricted to allowed domains.
endpoint Submit URL. Default https://formgong.com/submit.
timeoutMs, signal, fetch, headers Transport controls. The default timeout is 15 s.

It resolves to { success: true, id, lang, message } and throws FormgongError on failure.

FormgongError

Property Description
code Stable code. Server codes: missing_access_key, unknown_access_key, invalid_json, too_many_fields, too_large, origin_not_allowed, rate_limited, limit_exceeded, turnstile_missing, turnstile_failed, uploads_disabled, file_unsafe, … Client codes: network_error, timeout, aborted, invalid_response.
status HTTP status, or 0 for network errors.
message Localized, human-readable text that is ready to show.
retryable true for network problems, rate limits and 5xx responses.

Other helpers:

  • trySubmit(): the same as submit but never throws. It returns { ok, result | error }.
  • createClient(accessKey, defaults): binds an access key and default options once.
  • honeypotTriggered(data): tells you whether a honeypot field is filled in.

Anti-spam: createTracker(target, accessKey, options?)

It counts keydowns, pastes and the distinct fields typed into, starting from when the form is seen. On the first interaction it fetches a signed challenge from GET /pow?k=… and solves it in the background with Web Crypto (SHA-256, about 13 bits, typically a fraction of a second). Call tracker.signals() to get { _fg_b, _fg_pow }. It waits up to 2 s for the proof-of-work, like fg.js.

Formgong only scores these signals when _fg_b is present. Server-side and SDK posts without them are never penalised, so never create a tracker on a server and never fake the values.

Lower-level helpers: solvePow(challenge), fetchPowChallenge(accessKey), leadingZeroBits().

Account API: @formgong/core/api

import { FormgongApi } from "@formgong/core/api";

const api = new FormgongApi({ token: process.env.FORMGONG_TOKEN }); // fgp_… personal API token
const { form } = await api.createForm({ name: "Contact – acme.com" });
const { code } = await api.getFormSnippet({ form_id: form.id, framework: "react" });
const { submissions } = await api.listRecentSubmissions({ form_id: form.id, limit: 5 });

This talks JSON-RPC to the Formgong MCP endpoint (POST https://formgong.com/mcp). Create tokens under Dashboard → Account → API tokens. Personal tokens are secrets: use them on the server, in scripts or in CI, never in a browser. Limits: 60 requests per minute per token and 10 new forms per hour.

Server-side notes

From a server (Node, Workers, Server Actions), per-IP rate limits apply to your server's IP: 20 submissions per minute per IP and 30 per minute per form. If the form only allows certain domains, pass origin. For Next.js, use @formgong/next, which forwards Origin and Accept-Language for you.

Why Formgong

Formgong is a hosted form backend with a free plan, built for static and AI-built sites. Submissions go to email and Telegram, plus webhooks for Make, n8n and Zapier. Data is stored in the EU, and messages and autoreplies come in 12 languages. Spam filtering includes a honeypot, optional Cloudflare Turnstile and cookie-free behaviour signals, with no stored fingerprints.

How it compares with Formspree, Web3Forms, Basin, Getform, FormSubmit and Netlify Forms: formgong.com/en/compare. Moving from Formspree takes one line: change the form action or the access key (guide).

Packages

Package What it is
@formgong/core Tiny typed client: submit(), typed errors, anti-spam tracker, account API
@formgong/react <ContactForm>, <FormgongForm>, useFormgong() for React, Lovable, Bolt and v0
@formgong/next Next.js Server Action and Route Handler helpers, plus App Router components
@formgong/vue Vue 3 and Nuxt components and the useFormgong() composable
@formgong/svelte use:formgong action, store helper, Svelte components
@formgong/astro Astro components with progressive enhancement, plus a helper for Actions
formgong CLI: npx formgong init creates a form and adds it to your project
create-formgong npm create formgong@latest: starter projects

Links

License

MIT © Formgong

Related Packages

formgong

Formgong CLI: create a form backend and add a working contact form to Next.js, R...

504 0
@formgong/vue

Vue 3 / Nuxt contact form without a backend: , and useFormgong(). Submissions go...

157 0
consentlayer

Consent-first analytics for the browser and server: one API in front of GA4, Met...

0 1
@hellyeah/x-ray

Multi-platform analytics SDK for browser, React, Next.js, Remix, Vue, Svelte, Nu...

20,021
dewee-webchat

Embeddable chat widget for Dewee AI agents — web component, React/Vue/Astro wrap...

512 7

Version History

Version Released License
0.1.0 MIT