Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Sending Data with HTMX: hx-vals, hx-include, and hx-params

Welcome to the next lesson in our journey from React to HTMX. So far, we have assembled a powerful toolkit for controlling AJAX requests. We know how to specify what request to make (hx-get, hx-post), where to place the response (hx-target), how to swap it in (hx-swap), and, from our last lesson, when to send the request (hx-trigger).

This lesson addresses the final piece of the puzzle: defining what data gets sent to the server. In a React application, you'd typically gather state from various components, assemble a payload object, and pass it to a fetch call. HTMX, true to its declarative nature, provides a set of attributes to manage this directly in your markup. We will explore the three key attributes for this purpose: hx-include, hx-vals, and hx-params. Mastering these will give you complete control over the information your server receives.

The Default: Automatic Inclusion with Forms

Before we dive into special attributes, it's important to recognize HTMX's "sensible default" behavior. HTMX is built on the grain of HTML. If an element that triggers a request is located inside a <form> tag, HTMX will automatically include the values of all named inputs within that form in the request. This is the most common and intuitive way to send data.

This behavior aligns perfectly with the principle of progressive enhancement. A standard HTML form will work without JavaScript, and HTMX simply enhances it to submit via AJAX without a full page reload.

Sending Data in HTMX Requests (3 Ways) - DEV Community

This article from the DEV Community clearly explains the default form-based data submission strategy.

Please read the first section, Using a form Tag. Pay attention to the simple structure and the note about the Express.js middleware bodyParser.urlencoded, which you'll already have in your stack from our earlier lessons.

hx-include: Including Values from Anywhere

The default form behavior is great, but modern UIs often have controls spread across the page. Imagine a search bar in the header, filter options in a sidebar, and the "search" button near the content area. The data you need to send isn't always neatly contained in a single form with the triggering element.

This is where hx-include comes in. It allows you to use a CSS selector to specify other elements on the page whose values should be included in the request.

Sending Data in HTMX Requests (3 Ways)

This video provides a practical demonstration of hx-include, showing how you can link a button to a distant input field.

Watch the section that explains hx-include, starting from this introduction to the end of the example. Notice how a simple ID selector is used to connect the button and the input.

The power of hx-include lies in the flexibility of CSS selectors. You can target a single element by its ID (#my-input), a group of elements by a class (.filter-controls), or even use more complex relational selectors.

For example, in a "bulk operations" scenario where you have a table of users with checkboxes, a "Delete Selected" button could use hx-include to gather the IDs of all checked users.

Mastering hx-get and hx-post

This resource from the "ASP.NET Core Reimagined with htmx" book (the concepts are language-agnostic) shows several powerful use cases for hx-include.

Skim the following examples to see the versatility of hx-include: The basic explanation under the heading Including Form Fields. Note the variety of possible selectors. The Bulk Operations example, which uses hx-include="[name='ids']:checked" to collect values only from checked checkboxes. The Inline Editing example, which uses hx-include="closest tr" to gather all inputs from the same table row as the save button.

This declarative approach of connecting distant elements is a cornerstone of the HTMX philosophy, replacing the imperative JavaScript logic you would write to find those DOM elements and extract their values.

hx-vals: Sending Arbitrary or Computed Data

What if the data you want to send doesn't exist in an input field? You might need to send a hardcoded flag, a piece of metadata, or a value computed by JavaScript. For this, HTMX provides the hx-vals attribute.

hx-vals allows you to add a JSON object to the parameters of a request. You can define this JSON directly in the attribute.

A particularly powerful feature, especially given your JavaScript expertise, is the js: prefix. This allows you to define the value of hx-vals using a JavaScript expression that returns an object.

Sending Data in HTMX Requests (3 Ways)

The same video also has an excellent segment on hx-vals, including a clear demonstration of the js: prefix.

Please watch the section on hx-vals, from its introduction. The demonstration of calling a JavaScript function (get_password) to generate a value is a key technique to remember.

Using hx-vals is the HTMX equivalent of manually constructing a payload object in a React fetch call. It's the designated escape hatch for when the data you need isn't directly represented in the DOM.

hx-params: Filtering Submitted Parameters

hx-include and hx-vals are about adding parameters to a request. hx-params does the opposite: it filters them.

By default, HTMX sends all parameters it finds in an enclosing form. hx-params lets you control which parameters are actually included. You can use it to either include only a specific list of parameters or to exclude certain parameters.

The syntax is hx-params="<param1>, <param2>" to include only those, or hx-params="*" to include all (the default), or hx-params="not <param1>, <param2>" to exclude specific ones.

This is useful in large, complex forms where a single button or action should only submit a subset of the form's data, preventing over-posting.

Mastering hx-get and hx-post

The "Mastering hx-get and hx-post" resource provides a concise explanation of this attribute.

Find the heading Controlling Which Parameters Are Sent and review the short example. This introduces the attribute's purpose and syntax.

Synthesis: A Practical Filtering Example

Let's bring these concepts together with the "My Fantastic Blog" example. Imagine we want to add filtering by year, and also have a checkbox to show only list-style items.

This shows a blog page with filter buttons for different years.

Our UI has filter buttons for "2023", "2022", etc. Clicking a year button should fetch and display only the posts from that year. Let's also add a hypothetical checkbox somewhere else on the page: <input type="checkbox" name="showOnlyList" value="18" id="show-only-list-check">.

A filter button could be structured like this:

<button
    hx-get="/blog"
    hx-target="#post-list"
    hx-swap="innerHTML"
    hx-include="#show-only-list-check"
    hx-vals='{"year": 2023, "page": 1}'
>
    2023
</button>

Let's break down what happens when this button is clicked:

  1. hx-get="/blog": HTMX initiates a GET request to the /blog endpoint.
  2. hx-include="#show-only-list-check": HTMX finds the element with the ID show-only-list-check. If it's checked, it adds its name (showOnlyList) and value (18) to the request parameters.
  3. hx-vals='{"year": 2023, "page": 1}': HTMX adds the year and page parameters from this JSON object to the request. If a parameter name conflicts with one from hx-include, the hx-vals value takes precedence.

The server will receive a request where the URL looks something like this:
http://localhost:3000/blog?showOnlyList=18&year=2023&page=1

Your Express handler for /blog can then read these query parameters (req.query.showOnlyList, req.query.year, req.query.page) to filter the data and render the correct HTML partial for the post list.

This screenshot of the browser's developer tools shows how the different parameters are combined into a single GET request URL.

This single, declarative button elegantly combines data from a hardcoded value (year), a default value (page), and a separate element on the page (showOnlyList), without a single line of custom JavaScript.

Conclusion

You now have a complete model for constructing HTMX requests. You can control the method, target, swap behavior, trigger, and crucially, the data payload.

Key Takeaways:

  • Implicit Inclusion: By default, HTMX includes all inputs from an enclosing <form>.
  • hx-include: Use this to add data from other elements on the page, identified by CSS selectors. It's ideal for structurally separate but logically related inputs.
  • hx-vals: Use this to add arbitrary data, either as a static JSON object or dynamically computed using the js: prefix. It's your "escape hatch" for data not present in DOM inputs.
  • hx-params: Use this to filter which parameters from a form are sent. It provides fine-grained control to prevent over-posting.

With these tools, you can declaratively assemble any request payload you need, directly from your HTML. This completes our tour of the core HTMX request mechanics.

In the next module, we will put all this knowledge into practice. We'll move from individual attributes to building complete, robust features as we tackle the essential CRUD (Create, Read, Update, Delete) patterns that form the backbone of most web applications.

Can't find a good explanation? Sign up and we'll make it for you

Sign up