Hello! In our last two lessons, we focused on the "Read" part of CRUD operations. You learned how to fetch an entire list of items on page load and then how to implement efficient server-side pagination to handle large datasets. These patterns, centered on hx-get, form the backbone of displaying information in a hypermedia application.
Today, we shift our focus to the "Create" operation. Our goal is to implement one of the most common UI patterns: an inline form that allows a user to add a new item to a list without a full page reload. You will learn how to use the hx-post attribute to send form data to your Express server, which will then create the new item and return an HTML fragment to be appended seamlessly to the existing list. This lesson directly contrasts the declarative, server-centric HTMX approach with the client-side state management and manual data fetching you're familiar with from React.
The Core Pattern: POST, Render, Swap
The hypermedia approach to creating new data is elegant and straightforward. It relies on a few key HTMX attributes working in concert with a server endpoint.
- The Form: An HTML
<form>contains the input fields for the new item. - The POST Request: Instead of a traditional form submission, an HTMX attribute (typically on the form or its submit button) triggers an AJAX
POSTrequest.hx-postspecifies the URL endpoint to send the data to. HTMX automatically includes all the data from the enclosing form in the request body. - The Server Endpoint: An Express route handles the
POSTrequest. It extracts the new item's data from the request body, saves it to the database (or in-memory store), and then renders an HTML partial representing just the newly created item (e.g., a single<li>or<tr>). - The Target and Swap: The original form specifies where this new HTML fragment should go using
hx-target(pointing to the list container) and how it should be added usinghx-swap(e.g.,beforeendto append it to the list).
This video from Net Ninja provides a fantastic end-to-end walkthrough of this exact pattern. It demonstrates building the form, wiring up the HTMX attributes, creating the Node.js/Express handler, and seeing the result.
HTMX Tutorial for Beginners #5 - POST Requests
This video covers the entire "create" workflow. We'll watch it in a few key segments.
First, watch how the basic HTML form is structured from the start. Note that it's a standard <form> with named <input> fields. Next, see how hx-post is added to trigger the request. The explanation from this section is crucial; it highlights how HTMX automatically gathers form data. Then, skip ahead to understand the client-side of the swap. From this segment, focus on the explanation of hx-target and hx-swap="beforeend". This tells HTMX to take the server's response and append it inside our book list. Finally, watch the complete flow in action from the final demo.
As you saw, the key attributes work together beautifully:
hx-post="/books": On click, send a POST request to/books.hx-target="#book-list": Target the element with the IDbook-listfor the update.hx-swap="beforeend": Take the HTML from the response and insert it just inside the end of the target element.
Server-Side: Handling the POST
The server-side logic is just as important. The Express endpoint needs to be configured correctly to receive the form data, process it, and return the appropriate HTML fragment.
A critical piece of this puzzle is middleware. When a standard HTML form is submitted via HTMX, the data is sent with a content-type of application/x-www-form-urlencoded. Your Express server needs to be told how to parse this format. Without the correct middleware, req.body will be undefined. Given your experience, you know how vital correctly configured middleware is.
This video from DevTalk with FK clearly demonstrates this exact problem and its solution.
Sending Data in HTMX Requests (3 Ways)
This video clearly shows a common "gotcha" when setting up an Express server for HTMX forms and how to fix it.
Watch the segment from this timestamp. The presenter realizes the submitted form data is undefined on the server and correctly identifies that the express.urlencoded() middleware is missing. This is the key to parsing form data from HTMX.
So, the essential server-side setup in your app.js or server.js must include:
const express = require('express');
const app = express();
// This middleware is required to parse url-encoded form data
app.use(express.urlencoded({ extended: true }));
// ... your other routes
app.post('/items', (req, res) => {
// Thanks to the middleware, req.body contains the form data
const { newItemName } = req.body;
// 1. Create the new item and save it to the database.
// Let's assume this returns an object: const newItem = { id: 123, name: newItemName };
// 2. Render a partial template for JUST the new item.
// Assuming you have an EJS partial named '_item.ejs'
res.render('partials/_item', { item: newItem });
});
This is the second half of the hypermedia contract: the server doesn't return JSON, but a ready-to-insert piece of the UI.
Refining the Swap: afterbegin vs. beforeend
In the video, hx-swap="beforeend" was used to add the new book to the bottom of the list. This is often the desired behavior. However, in many applications (like a to-do list or a social media feed), you want the newest item to appear at the top.
For this, you can use hx-swap="afterbegin". This inserts the response HTML just inside the beginning of the target element.
The example in the marcusoft.net tutorial on building a to-do app uses this approach.
www.marcusoft.net – Learning by sharing since 2006
This article shows a form that prepends new items to a list.
Examine <tf start="id="todo-form" hx-on" end="Add Todo">the first code block. Notice the attributes on the <form> tag itself: hx-post="/todo", hx-target="#todo-list", and hx-swap="afterbegin". This configuration will add the new to-do item to the top of the #todo-list element.
UX Polish: Resetting the Form After Submission
After a user successfully adds an item, the input fields should be cleared. HTMX provides a simple way to do this using the hx-on attribute to listen for HTMX-specific events.
The simplest approach is to use the htmx:afterRequest event (which can be written as hx-on::after-request) and call the form's built-in reset() method.
<form hx-post="/todo" ... hx-on::after-request="this.reset()">
<!-- inputs -->
<button type="submit">Add</button>
</form>
However, as your application logic grows, this simple approach can have unintended side effects. For instance, if you add inline validation that also makes requests, this.reset() might fire when you don't want it to. A more robust solution involves adding a bit of JavaScript to check the details of the event.
The marcusoft.net article encounters this exact problem and provides a robust solution that is worth understanding.
www.marcusoft.net – Learning by sharing since 2006
This resource dives into a more advanced and reliable way to reset a form, which is appropriate for complex applications.
First, read the section "The form reset" to understand the problem. The author explains why this.reset() can be problematic when a form has multiple HTMX triggers. You can find it between these lines. Next, examine the author's solution. Instead of this.reset(), they use a small JavaScript snippet that inspects the event.detail object to ensure the form is only reset after a successful primary POST request. Read the final code block for the new.ejs file, starting from <tf start="id="new-form" class="todo-form"" end="getElementsByName('duedate')[0].value = ''; }">this implementation.
This pattern of using small, targeted JavaScript snippets within hx-on when declarative attributes aren't quite enough is a powerful aspect of HTMX. It allows you to maintain the hypermedia flow while handling the inevitable edge cases of a real-world UI.
Conclusion
You have now implemented the "Create" functionality for your list, a major milestone in any CRUD application. You've seen how to leverage hx-post to send data from a form, how to correctly configure your Express server to handle it, and how to use hx-swap to append the resulting UI fragment to your list without a page refresh.
Key Takeaways:
hx-postis the primary attribute for sendingPOSTrequests to create new resources.- HTMX automatically packages data from an enclosing
<form>into the request. - The
express.urlencoded()middleware is essential for parsing this form data on your Node.js server. - The server responds with an HTML partial of the newly created item, not the whole list or a JSON object.
hx-swapstrategies likebeforeend(append) andafterbegin(prepend) give you control over where the new item appears in the list.- Use
hx-on::after-requestfor post-submission tasks like clearing the form, with the option to use inline JavaScript for more complex logic.
In our next lesson, we will tackle the "Update" part of CRUD. You will learn how to implement an "edit-in-place" feature, where clicking an "Edit" button swaps an item's display view with a form, allowing the user to update it using hx-put.
Can't find a good explanation? Sign up and we'll make it for you
Sign up