formsync
v0.3.0
Published
The Easiest Way to Accept Form Submissions
Downloads
273
Maintainers
Readme
FormSync
FormSync is the easiest way to accept and manage form submissions without building a backend.
👉 Create your first form at https://formsync.app
Installation
npm install formsync
# or
pnpm add formsync
# or
yarn add formsync⚠️ Migration from v0.1.x
In v0.2.0, the package has been refactored into a multi-framework SDK. The React hook has moved to a subpath export.
Before:
import { useFormSync } from "formsync";After:
import { useFormSync } from "formsync/react";Framework Usage
Server SDK
Use the root package when you want to talk to the FormSync Public API from a server or backend runtime.
import { FormSync } from "formsync";
const formsync = new FormSync({
apiKey: process.env.FORMSYNC_KEY!,
});
const forms = await formsync.forms.list();
const submissions = await formsync.submissions.list({
formId: "your-form-id",
});The server SDK uses the public API base URL by default:
https://api.formsync.app/public/v1You can still keep using submitForm from the same root export for framework-agnostic client submissions.
React
import { useFormSync } from "formsync/react";
function MyForm() {
const { submit, isLoading } = useFormSync({
formId: "your-form-id",
onSuccess: (res) => alert("Success!"),
onError: (err) => alert("Error: " + err.message),
});
return (
<form onSubmit={submit}>
<input name="email" type="email" required />
<button type="submit" disabled={isLoading}>
{isLoading ? "Sending..." : "Submit"}
</button>
</form>
);
}Vue
<script setup>
import { useFormSync } from "formsync/vue";
const { submit, isLoading } = useFormSync({
formId: "your-form-id",
});
</script>
<template>
<form @submit="submit">
<input name="email" type="email" required />
<button type="submit" :disabled="isLoading">
{{ isLoading ? "Sending..." : "Submit" }}
</button>
</form>
</template>Svelte
<script>
import { formSync } from 'formsync/svelte';
let isLoading = false;
</script>
<form use:formSync={{ formId: 'your-form-id' }}>
<input name="email" type="email" required />
<button type="submit">Submit</button>
</form>Angular
import { Component, inject } from "@angular/core";
import { FormSyncService } from "formsync/angular";
@Component({
selector: "app-my-form",
template: `
<form (submit)="handleSubmit($event)">
<input name="email" type="email" required />
<button type="submit" [disabled]="formSync.isLoading()">
{{ formSync.isLoading() ? "Sending..." : "Submit" }}
</button>
</form>
`,
})
export class MyFormComponent {
formSync = inject(FormSyncService);
async handleSubmit(event: Event) {
event.preventDefault();
const formData = new FormData(event.target as HTMLFormElement);
await this.formSync.submit(formData, { formId: "your-form-id" });
}
}Astro
---
// Server-side (optional)
---
<form id="my-form">
<input name="email" type="email" required />
<button type="submit">Submit</button>
</form>
<script>
import { handleFormSubmit } from 'formsync/astro';
const form = document.getElementById('my-form');
form?.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
await handleFormSubmit(formData, { formId: 'your-form-id' });
alert('Submitted!');
});
</script>Remix
import { handleFormSync } from 'formsync/remix';
import { json } from '@remix-run/node';
export async function action({ request }: ActionFunctionArgs) {
const result = await handleFormSync(request, { formId: 'your-form-id' });
return json(result);
}
export default function MyForm() {
return (
<Form method="post">
<input name="email" type="email" required />
<button type="submit">Submit</button>
</Form>
);
}Core Module (Framework-Agnostic)
If you aren't using a specific framework adapter, you can use the core module directly.
import { submitForm } from "formsync";
const formData = new FormData();
formData.append("email", "[email protected]");
await submitForm(formData, {
formId: "your-form-id",
onSuccess: (res) => console.log("Success"),
onError: (err) => console.error(err),
});