Skip to main content

Fillo vs Formspree

How Formspree and Fillo differ on who renders the fields, validation, file uploads, and pricing, and how to move a Formspree form to Fillo.

Formspree is a form backend: you write the form in HTML or JSX and post it to a Formspree endpoint that stores and routes submissions. Fillo renders the fields from one schema and applies the same rules to server validation, uploads, and responses.

At a glance

FormspreeFillo
Where it rendersYour own HTML or JSX, posting to a Formspree endpoint. @formspree/react adds a useForm hook (React library)Native fields rendered from a Fillo schema by @usefillo/react or @usefillo/dom, or a Fillo-hosted page
Who writes the fieldsYou write every inputFillo renders them from the schema. You can swap any field kind for your own component
Form definitionYour markupFillo editor, or a typed defineForm() schema in your repo
ValidationClient checks you write, plus server-side errors returned by FormspreeOne schema drives client and server validation
File uploadsPaid plans only, 25 MB per file, stored in Formspree against the plan's storage (file uploads)500 MB per file by default, configurable up to 5,000 MB, sent from the browser to your Google Drive, Box, S3, or R2
ResponsesSubmissions dashboard with email notificationsResponse grid with answers, app context, files, versions, and delivery state
PricingMonthly submissions per plan, 50 on Free (plans)Free includes 200 completed responses a month. Everything starts at €35 or $39 a month for 5,000, with 25,000 and 100,000 tiers. Going over the allowance never stops a form or adds a charge
IntegrationsWorkflow plugins such as webhooks, Slack, Airtable, and Zapier (plugins)Signed webhooks, a Management API, native Slack, Notion, Google Sheets, HubSpot, and Discord destinations, plus Zapier and n8n apps

When Fillo fits

  • The form still needs to be built, and you want the fields rendered from one schema.
  • The same field IDs and rules should drive the UI, server checks, responses, and exports.
  • A teammate should be able to edit the form after launch without a deploy.
  • Respondents send files over 25 MB, or the files belong in your own storage.

When Formspree is enough

  • The markup is finished and the only missing piece is a submission endpoint.
  • Form state, layout, and client behavior must stay entirely in your own code.

Moving from Formspree

Fillo isn't a drop-in endpoint for existing markup, so a move means expressing the form as a Fillo schema. With Formspree you write the inputs and useForm() handles submit:

tsx
import { useForm } from "@formspree/react";

export function ContactForm() {
  const [state, handleSubmit] = useForm("your-form-id");
  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email">Work email</label>
      <input id="email" name="email" type="email" required />
      <button disabled={state.submitting}>Send</button>
    </form>
  );
}

With Fillo the fields come from the schema:

tsx
import { Fillo, createClient } from "@usefillo/react";
import "@usefillo/react/styles.css";

const fillo = createClient({ key: import.meta.env.VITE_FILLO_KEY });

export function ContactForm() {
  return (
    <Fillo.Form client={fillo} id="contact" title="Contact us">
      <Fillo.Email id="email" label="Work email" required />
    </Fillo.Form>
  );
}
  1. Export the Formspree submissions and files you need to keep.
  2. Recreate the fields as a schema, reusing your name attributes as field IDs.
  3. Render it on the same route with the default, styled, or headless renderer.
  4. Map custom validation and conditional UI to Fillo rules and test every path.
  5. Reconnect notifications and integrations, then send a test response.
  6. Switch traffic and keep the old form ready to restore until responses look right.

This page for agents: /docs/compare/formspree.md · index at /llms.txt

Updated

Was this page helpful?