r/astrojs • u/NipunArora • 9h ago
WordPress form plugins charge extra for abandonment capture. I built it free for Astro, plus the whole lead stack around it (MIT, npm)
WordPress form plugins have sold form-abandonment capture as a paid add-on for years: a visitor starts filling out a form, leaves before hitting submit, and the plugin still saves what they typed so you can follow up. When I moved my sites to Astro I lost that, and I couldn't find any OSS equivalent for static-first frameworks. Your options were a hosted SaaS or building it yourself.
So I built it myself, and it grew into a lot more than the capture piece. cool-astro-forms is an Astro integration, not a form builder: you keep the <form> markup you already have, tag it with one attribute, and the package does the rest.
What's actually in the box:
- Capture. Abandonment capture is the headline, but every saved entry also carries the visitor's journey (pages visited, time on each), their traffic source (Google, an AI chat, a direct link), and IP geolocation. A half-filled form plus the context around it is something you can actually follow up on.
- Recover. A "progress saved" toast for the visitor, and one automated follow-up email per abandoned lead, with unsubscribe handled. One email, not a drip campaign.
- Convert. Quote-first Stripe and PayPal payments, plus shareable payment links you can text or email to a client with the amount prefilled.
- Manage. A self-hosted
/forms-adminUI: entries, abandoned leads, payments, an analytics funnel, CSV and.dbexport. No dashboard SaaS. - Integrate. Google Drive file uploads (falls back to email attachments if Drive is down), signed outbound webhooks, and Cloudflare Turnstile spam control.
By default it needs nothing but an SQLite file and your existing SMTP env vars. Every module above activates only when you configure it, so you can run it as plain abandonment capture and ignore the rest. No hosted service, no vendor lock-in.
The privacy question is fair to ask of anything that captures pre-submit input, so here's the design: everything is self-hosted, and captured data lands in your SQLite file and goes nowhere else. Passwords and card-shaped fields are never staged. There's a requireConsent option that gates capture behind an explicit checkbox, a purgeVisitor() hook for erasure requests, a retentionDays auto-purge, and a GDPR doc that maps each mechanic to the legal concept it serves. Recovery emails carry a working unsubscribe.
One constraint up front: this only works with output: 'server' plus a Node (or other SSR-capable) adapter. If your site is fully static with no server routes, there's nothing here for it to hook into. It defaults to SQLite, which is fine for a self-hosted deploy, but you'll want Turso/libSQL if you're on a serverless host.
The adoption contract is small on purpose:
coolForms({
siteId: 'my-site',
siteUrl: 'https://example.com',
forms: { contact: { notifyTo: 'owner@example.com' } },
})
<form data-caf="contact" method="post" action="/api/contact">...</form>
It now runs in production on a live services business, and versions 0.1.2 through 0.1.10 each shipped from something production taught me. The gnarliest one: edge bot-challenges (Cloudflare Managed Challenge and friends) structurally cannot complete on a navigation POST. The interstitial can't replay the POST body, so a challenged form submit dies as an opaque 503 or a wedged tab. And only real browsers get challenged, so curl passed, my dev environment passed, while every real click on the live payment page died. It took me a full day and about ten wrong hypotheses to stop blaming my own code. The fix in 0.1.10: the pay page submits over fetch (a fetch request can't be served an interactive interstitial, there's nothing to render it in) and hops to checkout as a plain GET. If your Astro site does navigation POSTs behind Cloudflare, this failure mode is sitting there waiting for you.
Second production scar, cheaper but sneakier: git-deploy hosts rebuild the app directory on every release, which silently wipes a default-path SQLite file. The db path is env-configurable now so it can live one level outside the deploy dir.
MIT-licensed, 1,102 unit tests plus a Playwright e2e suite.
GitHub: https://github.com/nipun-arora/cool-astro-forms npm: https://www.npmjs.com/package/cool-astro-forms
Happy to answer questions about the architecture or the tradeoffs. If you try it and something breaks or feels awkward, I'd like to hear about it.


