← Templates

A contact form without a server, saving to your own database

A contact form without a server to run. One route checks each message and saves it as a row in your own Supabase table. It works without JavaScript, and its key can add rows and nothing else.

You want a contact form, not a dashboard to read it in

This one saves each message to a table in your own Supabase project. You read them in Supabase's Table Editor, and you can give your client a seat there too. We never hold the messages.

The whole template is public. Read it before you trust it.

Or scaffold it

$ npx wawesome init --template contact-form
$ npx wawesome deploy

What you get

A Function called root. The page is a plain file, public/index.html, with a form and no script. The form posts to the page’s own address, where one Hono route in src/index.ts checks the message with zod:

const Enquiry = z.object({
  name: z.string().trim().min(1, "Enter your name.").max(200, "Keep your name to 200 characters."),
  email: z.string().trim().pipe(z.email("Enter an email address like name@example.com.").max(320)),
  message: z.string().trim().min(1, "Enter a message.").max(5000, "Keep your message to 5000 characters."),
});

A good message becomes a row, and the visitor gets a 303 to the thank-you note. A wrong field gets a 422 that names it. If Supabase refuses the row, the answer is a 500 and the reason is in your logs.

Run it

npx wawesome init --template contact-form

It offers to log you in if you aren’t. Then it asks for a Function name, root by default, and an App slug. If you haven’t deployed yet, it offers to change your workspace address, which locks at your first deploy. Then it asks for two values, both in your Supabase project settings:

  • SUPABASE_URL, the project’s URL. We open that one host to your App’s outbound calls.
  • SUPABASE_PUBLISHABLE_KEY, the key that starts with sb_publishable_. It isn’t a secret, because schema.sql lets it add rows and nothing else.

Leave both blank and the page still goes live, and the form answers that it has no database yet. Set them later with npx wawesome env set.

Before the form can save anything, run schema.sql in your project’s SQL Editor. Then send a message and open Table Editor → enquiries. The row is there.

What to change first

The words in public/index.html. To add a field, add an input to the page, a rule to the zod schema, and a column to schema.sql.

What it doesn’t do

There’s no spam protection and no rate limit. Anyone with the publishable key can add rows directly, without the route’s checks. It doesn’t email you when a message arrives.

  • form
  • contact
  • database
  • supabase
  • hono
  • zod
  • website
  • typescript

Ready in about a minute

Sign in with GitHub, deploy, and get a public HTTPS endpoint.

Deploy this template