Contact Form#
The contact form on the About page is powered by Formspree.
Why Formspree#
GitHub Pages only serves static files: there is no server to receive a form submission and send an email. Formspree fills that gap. The form posts to a Formspree endpoint, and Formspree:
filters spam,
forwards each message to my email address,
keeps a copy in its dashboard, and
shows the visitor a confirmation page.
This keeps my email address off the site, so it cannot be harvested by bots. The free plan has a monthly submission limit, which is plenty for a personal site.
Alternatives considered:
mailto link: simplest, but publishes the email address.
Google Forms: free and unlimited, but the embedded form uses Google’s styling rather than the site theme.
Netlify Forms: built in, but only for sites hosted on Netlify.
Setup#
Sign up at formspree.io and create a new form.
Set the email address that submissions should be forwarded to.
Copy the form’s endpoint,
https://formspree.io/f/<form-id>. The form ID is not a secret: it is visible in the page source of any site that uses it.Add the form to the page (below), rebuild, and send a test message. Formspree asks you to confirm the first submission by email before forwarding any messages.
The form#
Sphinx has no form directive, so the form is plain HTML inside a raw directive in source/about.rst. The
classes (form-control, form-label, btn btn-primary) come from Bootstrap, which the PyData theme already
includes, so the form matches the site without any extra CSS:
.. _contact:
Contact
-------
.. raw:: html
<form action="https://formspree.io/f/<form-id>" method="POST" class="mt-3" style="max-width: 36rem;">
<div class="mb-3">
<label for="contact-name" class="form-label">Name</label>
<input type="text" id="contact-name" name="name" class="form-control"
maxlength="100" required autocomplete="name">
</div>
<div class="mb-3">
<label for="contact-email" class="form-label">Email</label>
<input type="email" id="contact-email" name="email" class="form-control"
maxlength="200" required autocomplete="email">
</div>
<div class="mb-3">
<label for="contact-message" class="form-label">Message</label>
<textarea id="contact-message" name="message" class="form-control"
rows="6" maxlength="5000" required></textarea>
</div>
<input type="text" name="_gotcha" style="display: none" tabindex="-1" autocomplete="off">
<button type="submit" class="btn btn-primary">Send message</button>
</form>
Points to note:
name="email"lets Formspree set the reply-to address, so replying to the notification email goes straight to the sender.required,type="email"andmaxlengthgive basic validation in the browser before anything is sent._gotchais a honeypot field. It is hidden from people, but spam bots tend to fill in every field; Formspree silently discards any submission where it is not empty.The
.. _contact:label lets other pages link straight to the form with therefrole, as the “Contact form” link on the home page does.
Troubleshooting#
- “Form not found. Please check the form hashid”
The
actionURL still contains a placeholder or a mistyped ID. Copy the endpoint again from the Formspree dashboard, rebuild, and reload the page withCtrl+F5.- Messages are not arriving
Check that the first submission was confirmed, look in the spam folder, and check the Formspree dashboard to see whether the message was received.
- Testing locally
Submissions from
http://localhost:8000work the same as from the published site.