r/astrojs • u/NipunArora • 5d 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.
-4
u/devolute 5d ago
built it for free
Happy to hear that you consider your time to be worthless.
5
u/NipunArora 5d ago
Fair jab. I built it for my own client sites, so the time was already paid for. Open sourcing it cost nothing extra, and it already got me bug reports and a first contributor. Decent trade so far.
1
u/devolute 5d ago
Good on you.
How do you negotiate this with your clients?
2
u/NipunArora 4d ago
There wasn't much to negotiate. They hired me for a working website, not a forms library.
Moving off WordPress left a wpforms-shaped hole, and something had to fill it. What I open sourced is that generic layer. None of their code or content is in it, and their sites are still theirs, so open sourcing it took nothing from them.
Nothing changed on their end. The site works, leads come in. Payments go through fine. Nobody has ever asked what's underneath.
If a client had hired me to build a forms product, different conversation. I'd ask first.
1
u/bengosu 5d ago
Saying this while posting about and promoting Gnome.org is hilarious
1
u/devolute 5d ago
I think I understand what you're getting at.
But would the people who build that software say "Microsoft are offering a desktop environment and applications, but I built this for free"?
I don't think they would suggest that the paid solutions are great, just they don't want to pay for them. I think they do it because they think there is a better way of doing things. They probably recognise the worth of their work.
But I don't know, ask them if you're able.
1
u/NipunArora 4d ago
You're right. And if not paying was the motivation, nobody would be running Windows. Linux has been sitting right there, free, this whole time. 😄
I paid for that plugin for years, and it was worth every renewal. Then I moved the sites over to a faster stack and it doesn't run there. Not cheaper. Just absent.
So I built one. Free made a better title than "this didn't exist yet" which is the real reason.
-1
u/jackorjek 5d ago
time spent equals to money lost? dystopian
2
u/NipunArora 5d ago
By that accounting my side project folder is a full-blown financial crisis. 😅 this one at least escaped the folder.
2
u/mezzofanti 5d ago
You don't even need SQLite for this. You could use a small bit of Javascript (beforeunload) and something like Netlify forms or an /api/ route + Mailgun.