
A plugin for the Raku Air web framework that renders a contact form and sends submissions via SMTP.
Synopsis
#!/usr/bin/env raku
use Air::Functional :BASE;
use Air::Base;
use Air::Plugin::MailForm;
my $site =
site :register[$mail-form, LightDark.new],
page
main [
h3 'Contact Us';
$mail-form;
]
;
$site.serve;
Set environment variables before running:
export SMTP_USER=myuser
export SMTP_PASS=mypass
export MAIL_FROM=webserver@example.com
export MAIL_TO=contact@example.com
raku bin/29-mailform.raku
Description
Air::Plugin::MailForm is a two-class plugin. Air::Plugin::MailForm is the field-free base that provides SMTP sending and form lifecycle (validate → send → finish/retry). Air::Plugin::ContactForm is the built-in subclass that adds name, email, and message fields. The exported $mail-form singleton is a ContactForm instance, so most users need nothing more than use Air::Plugin::MailForm.
SMTP credentials and routing are configured entirely through environment variables so no secrets appear in source code.
Custom Fields
Subclass Air::Plugin::MailForm to define your own fields. Override method mail-body to control the email body text:
use Air::Plugin::MailForm;
use Air::Form;
class EnquiryForm is Air::Plugin::MailForm {
has Str $.company is required;
has Str $.phone is tel;
has Str $.message is multiline;
method mail-message(Str $from, Str $to, Str $subject --> Str) {
qq:to/END/;
From: $from
To: $to
Subject: $subject
Company: { $.company }
Phone: { $.phone }
{ $.message }
END
}
method form-routes {
self.init;
self.submit: -> EnquiryForm $form {
self.submit-form($form)
}
}
}
our $enquiry-form = EnquiryForm.empty;
# site :register[$enquiry-form], ...
# page main $enquiry-form;
Environment Variables
| Variable | Default | Description |
|---|
SMTP_HOST | mail-eu.smtp2go.com | SMTP server hostname |
SMTP_PORT | 587 | SMTP server port |
SMTP_USER | — | SMTP username |
SMTP_PASS | — | SMTP password |
MAIL_FROM | — | Envelope sender address |
MAIL_TO | — | Recipient address |
MAIL_BCC | — | BCC address (optional) |
MAIL_SUBJECT | Contact Form Submission | Email subject line |
Docker
Pass the variables at docker run time with -e flags — never bake secrets into the image:
docker run -p 3000:3000 \
-e SMTP_USER=myuser \
-e SMTP_PASS=mypass \
-e MAIL_FROM=webserver@example.com \
-e MAIL_TO=contact@example.com \
myimage
For multiple containers or CI pipelines an env file is tidier:
# .env (add to .gitignore — never commit this)
SMTP_USER=myuser
SMTP_PASS=mypass
MAIL_FROM=webserver@example.com
MAIL_TO=contact@example.com
docker run -p 3000:3000 --env-file .env myimage
Installation
If you already have Air working, then:
zef install Air::Plugin::MailForm
Otherwise, follow the Air::Examples Getting Started
cd Air-Examplesraku bin/29-mailform.raku- point browser to
http://localhost:3000
Author
librasteve librasteve@furnival.net
Copyright and License
Copyright 2026 Stephen Roe.
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.