How to Add a Contact Form to GitHub Pages
GitHub Pages is an excellent platform for hosting static websites - it's free, fast, and integrates seamlessly with your Git workflow. However, one common challenge developers face is adding a functional contact form since GitHub Pages doesn't support server-side processing. The good news? You can easily add a professional contact form to your GitHub Pages site without writing any backend code.
In this comprehensive guide, we'll show you how to implement a fully functional contact form on GitHub Pages using Static Forms, a form backend service that handles all the server-side logic for you. Your visitors will be able to send messages, and you'll receive them directly in your email inbox.
Why GitHub Pages Needs a Form Solution
GitHub Pages serves only static files - HTML, CSS, and JavaScript. While this makes it incredibly fast and secure, it means you can't process form submissions directly. Traditional form handling requires:
- Server-side code to process submissions
- A database to store form data
- Email sending capabilities
- Spam protection mechanisms
Static Forms solves all of these challenges by providing a dedicated API endpoint that handles form submissions for you.
Prerequisites
Before we begin, make sure you have:
- A GitHub Pages site (already deployed or ready to deploy)
- A Static Forms account - sign up for free
- Your API key from the Static Forms dashboard
Step 1: Create a Basic HTML Contact Form
Let's start with a simple contact form. Create a new file called contact.html in your GitHub Pages repository, or add this form to an existing page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Us</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
background-color: #f5f5f5;
}
.contact-container {
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 10px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #333;
font-weight: 500;
}
input[type="text"],
input[type="email"],
textarea {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
transition: border-color 0.3s;
}
input:focus,
textarea:focus {
outline: none;
border-color: #4A90E2;
}
textarea {
resize: vertical;
min-height: 120px;
}
button {
background-color: #4A90E2;
color: white;
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #357ABD;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.alert {
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
display: none;
}
.alert.show {
display: block;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
</style>
</head>
<body>
<div class="contact-container">
<h1>Get in Touch</h1>
<p class="subtitle">We'd love to hear from you. Send us a message and we'll respond as soon as possible.</p>
<div id="alertBox" class="alert"></div>
<form id="contactForm" action="https://api.staticforms.dev/submit" method="POST">
<div class="form-group">
<label for="name">Your Name *</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Your Email *</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="subject">Subject</label>
<input type="text" id="subject" name="subject" placeholder="What's this about?">
</div>
<div class="form-group">
<label for="message">Message *</label>
<textarea id="message" name="message" required placeholder="Tell us what's on your mind..."></textarea>
</div>
<!-- Static Forms Configuration -->
<input type="hidden" name="apiKey" value="YOUR_API_KEY_HERE">
<input type="hidden" name="replyTo" value="@">
<input type="text" name="honeypot" style="display:none">
<button type="submit" id="submitBtn">Send Message</button>
</form>
</div>
<script>
const form = document.getElementById('contactForm');
const submitBtn = document.getElementById('submitBtn');
const alertBox = document.getElementById('alertBox');
form.addEventListener('submit', async function(e) {
e.preventDefault();
// Disable submit button
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';
// Hide any previous alerts
alertBox.classList.remove('show', 'alert-success', 'alert-error');
try {
const formData = new FormData(form);
const data = Object.fromEntries(formData);
const response = await fetch('https://api.staticforms.dev/submit', {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (result.success) {
// Show success message
alertBox.textContent = 'Thank you! Your message has been sent successfully. We\'ll get back to you soon.';
alertBox.classList.add('alert-success', 'show');
// Reset form
form.reset();
} else {
throw new Error(result.message || 'Something went wrong');
}
} catch (error) {
// Show error message
alertBox.textContent = 'Oops! Something went wrong. Please try again.';
alertBox.classList.add('alert-error', 'show');
} finally {
// Re-enable submit button
submitBtn.disabled = false;
submitBtn.textContent = 'Send Message';
}
});
</script>
</body>
</html>Important: Replace YOUR_API_KEY_HERE with your actual API key from your Static Forms dashboard.
Step 2: Understanding the Form Configuration
Let's break down the key parts of the form:
The Action Attribute
<form action="https://api.staticforms.dev/submit" method="POST">This points to the Static Forms API endpoint that will handle your submissions.
Required Hidden Fields
<input type="hidden" name="apiKey" value="YOUR_API_KEY_HERE">Your API key authenticates your form with Static Forms.
Optional Configuration Fields
<input type="hidden" name="replyTo" value="@">The @ value tells Static Forms to use the email address from the form's email field as the reply-to address, making it easy to respond to messages.
Honeypot Spam Protection
<input type="text" name="honeypot" style="display:none">This hidden field catches bots that automatically fill out all form fields. Legitimate users won't see or fill this field.
Step 3: Add reCAPTCHA Protection (Optional)
For additional spam protection, you can add Google reCAPTCHA. First, get your reCAPTCHA keys from the Google reCAPTCHA Admin Console.
Add the reCAPTCHA script to your <head>:
<head>
<!-- Other head elements -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>Then add the reCAPTCHA widget to your form:
<div class="form-group">
<div class="g-recaptcha" data-sitekey="YOUR_RECAPTCHA_SITE_KEY"></div>
</div>Don't forget to configure your reCAPTCHA secret key in your Static Forms CAPTCHA settings.
For more details on implementing reCAPTCHA, check out our understanding reCAPTCHA guide.
Step 4: Deploy to GitHub Pages
Now let's deploy your contact form to GitHub Pages:
- Commit your changes:
git add contact.html
git commit -m "Add contact form with Static Forms integration"- Push to GitHub:
git push origin mainWait for deployment: GitHub Pages automatically deploys your changes. This usually takes 1-2 minutes.
Test your form: Visit
https://yourusername.github.io/yourrepo/contact.htmland submit a test message.
Step 5: Customize Your Form
Custom Success Redirect
Instead of showing a success message on the same page, you can redirect users to a thank-you page:
<input type="hidden" name="redirectTo" value="https://yourusername.github.io/yourrepo/thank-you.html">Create a thank-you.html page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thank You</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.thank-you-container {
text-align: center;
background: white;
padding: 60px 40px;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
}
h1 {
color: #333;
margin-bottom: 20px;
}
p {
color: #666;
font-size: 18px;
margin-bottom: 30px;
}
a {
display: inline-block;
padding: 12px 30px;
background-color: #4A90E2;
color: white;
text-decoration: none;
border-radius: 4px;
transition: background-color 0.3s;
}
a:hover {
background-color: #357ABD;
}
</style>
</head>
<body>
<div class="thank-you-container">
<h1>✅ Message Sent!</h1>
<p>Thank you for contacting us. We'll get back to you as soon as possible.</p>
<a href="/">Return to Home</a>
</div>
</body>
</html>Custom Email Subject
Set a custom subject line for better organization:
<input type="hidden" name="subject" value="New Contact from GitHub Pages Site">Or use a visible subject field (as shown in the main example) to let users set their own subject.
Advanced Features
File Attachments
Allow users to upload files with their messages:
<div class="form-group">
<label for="attachment">Attach File (Optional)</label>
<input type="file" id="attachment" name="attachment" accept=".pdf,.doc,.docx,.jpg,.png">
</div>Note: File uploads require encoding the form as multipart/form-data. See our file uploads guide for implementation details.
Custom Fields
Add custom fields by prefixing the name with $:
<div class="form-group">
<label for="company">Company Name</label>
<input type="text" id="company" name="$company">
</div>Webhook Integration
For advanced workflows, you can connect your form to other services using webhooks (paid plans: Pro/Advanced). This allows you to:
- Send submissions to Slack or Discord
- Add contacts to your CRM
- Trigger automation workflows
- Store data in Google Sheets or Airtable
Configure webhooks in your Static Forms dashboard.
Complete Working Example
Here's a complete, copy-paste-ready contact form for GitHub Pages:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact - My GitHub Pages Site</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about.html">About</a>
<a href="/contact.html">Contact</a>
</nav>
<main>
<section class="contact-section">
<h1>Contact Us</h1>
<p>Have a question or want to work together? Drop us a message!</p>
<form action="https://api.staticforms.dev/submit" method="POST" id="contactForm">
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email" required>
<input type="text" name="subject" placeholder="Subject">
<textarea name="message" placeholder="Your Message" required></textarea>
<!-- Static Forms Configuration -->
<input type="hidden" name="apiKey" value="YOUR_API_KEY_HERE">
<input type="hidden" name="replyTo" value="@">
<input type="text" name="honeypot" style="display:none" tabindex="-1" autocomplete="off">
<button type="submit">Send Message</button>
</form>
</section>
</main>
</body>
</html>Troubleshooting Common Issues
Not Receiving Email Notifications
If you're not receiving form submissions:
- ✅ Verify your API key is correct in the form
- ✅ Check your spam/junk folder
- ✅ Confirm your email is verified in Static Forms
- ✅ Test with different email addresses
Form Submission Fails
If submissions aren't working:
- ✅ Check the browser console for JavaScript errors
- ✅ Ensure the API endpoint URL is correct:
https://api.staticforms.dev/submit - ✅ Verify all required fields have the
requiredattribute - ✅ Make sure the honeypot field is properly hidden
CORS Errors
If you see CORS-related errors:
- ✅ Use HTTPS for your GitHub Pages site (enable in repository settings)
- ✅ Ensure you're using JSON format when submitting via JavaScript
- ✅ Include proper
Content-Typeheader:application/json
Best Practices for GitHub Pages Forms
- Keep it simple - Only ask for information you actually need
- Validate inputs - Use HTML5 validation and JavaScript for better UX
- Provide feedback - Show loading states and confirmation messages
- Mobile-friendly - Ensure your form works well on all devices
- Test thoroughly - Submit test forms before going live
Conclusion
Adding a contact form to your GitHub Pages site doesn't have to be complicated. With Static Forms, you can implement a professional contact form in minutes without any backend code or complex infrastructure.
Your form will handle submissions reliably, protect against spam with built-in security features, and deliver messages straight to your inbox - all while keeping your GitHub Pages site completely static.
Ready to add a contact form to your GitHub Pages site? Sign up for Static Forms and get started today with our free tier!
For more integration examples, check out our guides for Next.js, Gatsby, and Nuxt.js.
Related Articles
How to Add a Contact Form to Google Sites
Learn how to add a working contact form to your Google Sites website using Static Forms — no backend or coding experience required.
How to Add a Contact Form to Your Astro Website
Step-by-step tutorial for adding a working contact form to Astro sites using Static Forms — no backend required.
The Complete Guide to Contact Forms on JAMstack Sites
Learn how to add contact forms to JAMstack and static websites without a backend. Works with Next.js, Gatsby, Hugo, Jekyll, Astro, and more.