logo Contact

Static Site, Dynamic Soul: Building Forms with Elegance and Edge

On Jul 24, 2025 Hugo Cloudflare Workers HTMX Forms Serverless deployment frontend

The story starts with a familiar frustration—one that any web craftsman can relate to. A client had a beautiful, blazing-fast Hugo site, a masterpiece of static efficiency. But a single, necessary feature was costing them over $200 a month: their contact form. To us, this wasn’t just a budget issue; it was a matter of principle. Paying that much for a simple form felt like putting a clunky, expensive engine in a sleek, lightweight glider. It just didn’t feel right. Our craftsman’s intuition told us there had to be a more elegant solution, one that honored the simplicity of the original site. Our client wanted to cut costs without sacrificing functionality or user experience.

When they brought this challenge to our team at Wawandco, we immediately saw an opportunity. During our internal discussions, we realized they were already paying for Cloudflare for their CDN and security needs. This was the perfect foundation for a cost-effective solution.

The lightbulb moment came when we considered Cloudflare Workers. Here was a way to add serverless form processing directly into their existing infrastructure, without any additional monthly subscriptions or complex backend deployments.


The Blueprint: A Serverless Brain for a Static Site

After discussing the technical approach with our team, we decided to move forward with a simple yet powerful architecture that combines the best of three worlds:

  1. Hugo & HTMX: The lightning-fast frontend, with a sprinkle of HTMX magic to submit forms without a single page reload.
  2. Cloudflare Worker: The serverless “brain” living on the edge. It acts as a smart mailman, catching the form data, figuring out what it is, and forwarding it to the right place.
  3. An External Service: The final destination. This could be anything from an email service like Mailgun to a CRM or even a simple Slack channel.

Here’s how the message flows: User Submits FormHTMX sends data to /api/formCloudflare Worker processes itWorker forwards it to their serviceWorker sends back a “Thank You” messageHTMX displays it instantly.

Let’s build it. 🚀


Step 1: Crafting the Frontend with Hugo and HTMX

A backend is only half the story. The user experience has to be seamless. A full-page reload after submitting a form feels archaic. This is where HTMX comes in—it’s the secret weapon for modernizing our static site.

In an era of complex JavaScript frameworks, HTMX is a breath of fresh air that embodies the artisan spirit. It lets us achieve modern, dynamic interactions using simple HTML attributes. There’s an undeniable elegance to adding hx-post and hx-swap to a standard tag and watching it come alive. It’s a reminder that you don’t always need heavy machinery to do beautiful work.

Here’s the form layout in our Hugo template:


<form hx-post="/api/form" hx-swap="outerHTML" class="max-w-xl mx-auto">

  <div class="mb-4">
    <label for="Email" class="block text-sm font-medium">Email<span class="text-red-500">*</span></label>
    <input type="email" id="Email" name="Email" required class="w-full px-3 py-2 border rounded-md">
  </div>

  <div class="mb-4">
    <label for="Name" class="block text-sm font-medium">Name</label>
    <input type="text" id="Name" name="Name" class="w-full px-3 py-2 border rounded-md">
  </div>

  <button type="submit" class="w-full bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
    Submit
  </button>
</form>

<script src="https://unpkg.com/[email protected]/dist/htmx.min.js"></script>
  • hx-post="/api/form" tells HTMX where to POST the form data.
  • hx-swap="outerHTML" tells HTMX to replace the entire <form> element with whatever HTML the worker sends back.

The user clicks “Submit,” and poof—the form vanishes, replaced by a friendly, personalized “Thank you” message. No flicker, no reload. Just smooth, instant feedback.

Step 2: Building the Serverless Brain (The Cloudflare Worker)

The heart of our solution is a single JavaScript file, functions/api/form.js, which Cloudflare runs at the edge. This worker’s job is to be a vigilant gatekeeper and an intelligent processor.

First, it validates the request, parses the form data, and then customizes a response based on the submitted fields. Finally, it forwards the data to its final destination.

Here is the complete code for the worker:


// functions/api/form.js
export async function onRequest(context) {
  const { request, env } = context;

  // The form's final destination, stored securely in Cloudflare's environment variables
  const TARGET_URL = env.FORM_TARGET_URL;

  // Rule #1: We only accept POST requests.
  if (request.method !== 'POST') {
    return new Response('Only POST requests are allowed', { status: 405 });
  }

  try {
    // Parse the form data from the request.
    const form = await request.formData();
    let formData = Object.fromEntries(form.entries());

    // Add some useful context: where did this submission come from?
    formData.referralUrl = request.headers.get('referer') || 'Unknown';

    // --- Intelligent Form Routing ---
    // Customize messages based on the form content.
    let userMessage = '';
    let internalMessage = '';

    // A simple newsletter subscription (only Email is filled)
    if (formData.Name === '') {
      userMessage = `<p>Welcome aboard, ${formData.Email}! You're now subscribed.</p>`;
      internalMessage = `📰 New Subscriber: ${formData.Email} joined from ${formData.referralUrl}`;
    }
    // A more detailed contact form (Name is provided)
    else {
      userMessage = `<p>Got it, ${formData.Name}! Thanks for your message. We'll get back to you soon.</p>`;
      internalMessage = `📝 New Contact Form from ${formData.Name} [${formData.Email}] via ${formData.referralUrl}`;
    }

    // --- Forward the Data (Fire and Forget) ---
    // We wrap our fetch call in context.waitUntil(). Why? Because the user
    // doesn't need to wait for our server to talk to a third-party service.
    // Their part of the transaction is done. waitUntil tells the Cloudflare
    // network, "Send the success message back to the user immediately, and then
    // finish this other task in the background." It's a conscious decision to
    // prioritize the user's perceived speed and respect their time.
    context.waitUntil(
      fetch(TARGET_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ text: internalMessage }), // Example for a Slack webhook
      })
    );

    // --- Send a Success Response to HTMX ---
    // HTMX will swap the form with this HTML.
    return new Response(userMessage, {
      headers: { 'Content-Type': 'text/html' },
    });

  } catch (error) {
    // Handle any unexpected errors gracefully.
    const errorMessage = '<p>Sorry, something went wrong. Please try again later.</p>';
    return new Response(errorMessage, {
      status: 500,
      headers: { 'Content-Type': 'text/html' },
    });
  }
}

This single, versatile worker now powers every form on their site: newsletter signups, contact pages, product inquiries, and mobile-specific forms.

The Payoff: A Dynamic Experience on a Static Foundation

The implementation was a complete success. By combining Hugo, Cloudflare Workers, and HTMX, we achieved the perfect balance for our client. They kept all the benefits of their static site:

  • Incredible Speed: Their site remained just static files, served from Cloudflare’s global edge network.
  • Ironclad Security: The attack surface stayed minimal. No database, no complex server to patch. The FORM_TARGET_URL is stored securely as an environment variable in Cloudflare, not hard-coded in the repository.
  • Effortless Scalability: If a million people visit their contact form tomorrow, Cloudflare won’t even break a sweat.
  • Cost-Effective: Since they were already paying for Cloudflare, this solution added virtually no additional cost.

The result? Our client went from spending over $200 per month on form handling to essentially $0 in additional costs. They were thrilled with both the cost savings and the improved user experience. The client expressed deep appreciation for our team’s innovative approach and technical execution.


Conclusion

In the end, we didn’t just solve a technical problem; we restored harmony to the project. The site was once again a cohesive whole—fast, secure, and now, truly efficient from end to end. The real win wasn’t just the cost savings; it was the confirmation of our core belief: the most robust and beautiful solutions often arise from the thoughtful combination of simple, powerful tools. Before you reach for another third-party service, take a closer look at the tools you already have. The most elegant solution might just be hiding in plain sight, waiting for a craftsman to bring it to life.

When faced with similar challenges, consider looking at the infrastructure you’re already paying for—the answer might be closer than you think.

Further Reading

  • Cloudflare Workers Docs - Learn how to build and deploy serverless functions on Cloudflare’s global network.
  • HTMX Docs - Dive into the attributes that let you build modern user interfaces with simple HTML.
  • Hugo Documentation - Explore the world’s fastest framework for building websites.