HTMX on Cloudflare Pages

HTMX on Cloudflare Pages

Using HTMX and Cloudflare Workers is a great way to sprinkle in some dynamic functionality into your Cloudflare Pages static site. Let's look at an example of handling a form submission.

Important! Before proceeding with adding HTMX to your Cloudflare Pages site, please make sure you've properly secured your site from XSS attacks. For a starter guide on this topic see our post Adding Security Headers on Cloudflare Pages

{{< divider "Cloudflare Wrangler Config" >}}

First step is to make sure you have Cloudflare Wrangler installed to be able to test your Cloudflare Worker locally (install directions can be found here).

Now let's create a 'wrangler.toml' file in the base directory of your project. This configuration file lets Wrangler know how to load your site.

# wrangler.toml
name = "example"
pages_build_output_dir = "public"

Explanation of Settings

  • name: Can be anything, but best practice is to match the name of the app in Pages.
  • pages_build_output_dir: The directory where your static site files live. If using Hugo that's "public".

Next make sure any Cloudflare Worker scripts you want to run are within a '/functions' folder within your site's root directory. Then run the below command to have Wrangler load your site. Default will for your site to be at http://localhost:8788.

npx wrangler pages dev

Note: This will live reload changes to your files in '/functions' but not in the 'pages_build_output_dir' path.

{{< divider "Using HTMX" >}}

As an example, let's create an email submission form using HTMX.

Firstly, add a form to the static page using the HTMX 'hx-post' directive. Here, we're also including the form's 'action' attribute as well to provide progressive enhancement.

<form action="/emailList" hx-post="/emailList">
	<input type="email" name="email" placeholder="Enter your email">
	<button class="button" type="submit">Get Notified</button>
</form>

On the static webpage that includes the email form include a HTML head script link to HTMX.

It's strongly advised to include a 'nonce' for this file to help guard against XSS attacks. See "Option 2" on Adding Security Headers on Cloudflare Pages

<head>
	<script src="/path-to-HTMX/htmx.min.js"></script>
</head>

Lastly, create a new Cloudflare Worker at '/functions/emailList'.

Cloudflare Pages uses file based routing for the '/functions' folder. Meaning that Pages will automatically route requests to '/emailList' to your function '/functions/emailList'. Hence, why the HTML form above is posting to '/emailList'.

Below is an example of a Cloudflare Worker that expects to receive an email address form submission, stores in a database, and then returns a HTML snippet.

Reminder: Don't forget to sanitize user provided data before storing in your database. You'll thank me later :)

export async function onRequest(context) {

	// Initialize variables.
	const { request, env } = context
	
	// Check if the request is an OPTIONS request.
	if (request.method === 'OPTIONS') {

		// Respond with headers that indicate which HTTP methods are allowed.
		return new Response(null, { 
			headers: { 
				'Allow': 'POST, OPTIONS' 
			}, 
			status: 204 
		})
	}

	// Check if the request is a POST request.
	if (request.method === 'POST') {

		// Get the form data.
		const formData = await request.formData()

		// Get the email from the form data.
		const email = formData.get('email')
		
		// Handle your form data here (e.g., send an email, store in a database, etc.)
		console.log(`Email received: ${email}`)
		
		// Return whatever content you want HTMX to replace the form with.
		return new Response("Thank-you! We've saved your email address.", { status: 200 })
	}
	
	return new Response('Method not allowed', { status: 405 })
}

Note that in the above example we're handling both 'OPTIONS' and 'POST' methods. That's because HTMX will issue an 'OPTIONS' request prior to sending a 'POST' to make sure the server will accept a 'POST' method for the path.

Congratulations! You now have a dynamic form on your static Cloudflare Pages website.

Cheers 🥂

More Articles