Static sites need contact forms. Netlify offers Netlify Forms. Cloudflare offers email sending and turnstile spam protection, but nothing out of the box for forms end-to-end. I wrote a small package that combines these Cloudflare features. The contact form on this site uses it.

How it works

  • Each form is a few lines of config: a title, an email subject, and which fields are allowed and required.
  • The Worker only accepts submissions from the site’s own origin, checks the Turnstiledevelopers.cloudflare.com favicon token, and emails the submission through Cloudflare’s Send Email binding, with Reply-To set to the sender.
  • A Turnstile token has to be issued for the site’s hostname with the form’s name as its action, so a token from one form can’t be used to submit another.
  • A rejected submission returns an error that names the field to fix, rather than a generic failure.

The Vue side

  • useForm saves a draft to localStorage as you type, validates fields, shows a sending state, and resets the Turnstile widget after every attempt, because tokens are single-use.
  • useTurnstileWidget loads the Turnstile script once per page, however many forms there are.

Built with

TypeScript, Cloudflare Workers, Cloudflare Turnstile and Vue 3, with tests, published to GitHub Packages.

View repo