Starting a Blog with Hugo and Adding a Contact Form
- Patrick Kenekayoro
- Web development , Php
- March 11, 2024
Embarking on the journey of creating a personal blog or website often begins with the quest for simplicity and speed. In my pursuit, I stumbled upon a Reddit comment that said, “Just use Hugo!” This led me to discover the world of Hugo. This post marks the beginning of my Hugo-powered site.
Getting Started with Hugo
Despite having zero experience with Hugo, I managed to set it up in just one evening. Special thanks to Hugoplate from Zeon Studio for this theme.
Setting Up a Contact Form with Email Delivery
While the initial setup was swift, the most time-consuming aspect turned out to be configuring email delivery for the contact form.
What I could not do was Send it with ajax with my email API Keys in the wild ,
so here is how I did it.
Sending Emails in Static Generated Sites
Despite still utilizing Ajax (with Alpine.js) to submit forms, the actual email sending is now handled server-side through a compact PHP script.
Likely just as simple in most languages but the only thing harder than hosting index.php on a server is index.html.
HTML Form
Regular HTML form that submits via ajax
<form id="contact-form" action="/sendemail"
method="POST" x-data="contactForm()"
x-cloak @submit.prevent="submitData">
<div class="mb-6" x-show="submitted">
<p class="text-left font-medium tracking-normal text-emerald-600">Thank you!! I will get back as soon as possible</p>
</div>
<div class="mb-6">
<label for="name" class="form-label">
Message <span class="text-red-500">*</span>
</label>
<textarea
x-model="formData.message"
id="message"
name="message"
class="form-input"
placeholder="Message goes here..."
required
rows="8"></textarea>
</div>
</form>
<!-- Alpine.js -->
<script src="https://cdn.jsdelivr.net/npm/alpinejs@2.8.2/dist/alpine.min.js" defer></script>
Ajax Script
<script>
function contactForm() {
return {
formData: {
message: '',
},
submitted: false,
submitData() {
let form = document.getElementById('contact-form');
fetch('/sendemail', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(this.formData)
})
.then(() => {
this.submitted = true;
})
.catch(() => {
this.submitted = true
})
}
}
}
</script>
PHP Email Sending Script
The goal was a script with zero dependency, but I had difficulties reading environmental variables that stored the API keys, and so I opted to use Symfony’s DotEnv Component .
I have used SendGrid, but it can be easily adapted to any provider. Full code available here https://github.com/ktarila/sendgrid-email
require '../vendor/autoload.php';
use Symfony\Component\Dotenv\Dotenv;
$dotenv = new Dotenv();
$dotenv->load('../.env');
$allowedDomains = ['localhost'];
$currentDomain = $_SERVER['HTTP_HOST'];
$referrer = $_SERVER['REFERER'] ?? "http://{$currentDomain}/contact";
$sendgridApiKey = $_ENV['SENDGRID_API_KEY'] ?? '';
if ($_SERVER['REQUEST_METHOD'] === 'POST'
&& in_array($currentDomain, $allowedDomains)
) {
$recipientEmail = 'recipient@example.com';
$senderEmail = 'sender@example.com';
$subject = 'Contact form';
$json = file_get_contents('php://input');
$sendgridApiUrl = 'https://api.sendgrid.com/v3/mail/send';
if (!empty($json)) {
$data = json_decode($json, true);
$message = $data['message'] ?? '';
$email = $data['email'] ?? '';
$name = $data['name'] ?? '';
$content = "Email: {$email} \nName: {$name} \nMessage: {$message}";
}
if (!empty($message) && !empty($email) && !empty($name)) {
$data = [
'personalizations' => [
[
'to' => [
['email' => $recipientEmail]
]
]
],
'from' => [
'email' => $senderEmail
],
'subject' => $subject,
'content' => [
[
'type' => 'text/plain',
'value' => $content
]
]
];
$dataString = json_encode($data);
$ch = curl_init($sendgridApiUrl);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, $dataString);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
[
'Authorization: Bearer ' . $sendgridApiKey,
'Content-Type: application/json',
]
);
$response = curl_exec($ch);
curl_close($ch);
}
} else {
echo 'Not allowed';
}
header("Location: {$referrer}");
exit();
Nginx Configuration To complete the setup, configuring Nginx is required. The following snippet demonstrates how to process /sendemail with PHP while serving other paths directly:
server {
listen 80;
server_name severname.local;
location /sendemail {
root /full/path/send_email/public;
index index.php;
try_files $uri $uri/ /index.php?$query_string;
}
location ~ ^/index\.php(/|$) {
root /full/path/send_email/public;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal;
}
location / {
root /full/path/hugo/public;
index index.html;
try_files $uri $uri/ =404;
}
error_page 404 /404.html;
location = /404.html {
root /full/path/hugo/public;
internal;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /full/path/hugo/public;
internal;
}
}
Feel free to use this Markdown content for your blog or website. If you have any more requests or questions, let me know!