adjoe Engineers’ Blog
 /  Backend  /  htmx Tutorial
abstract illustration with code elements
Backend

How to Simplify Web Development with htmx

In adjoe’s WAVE team, we continuously run A/B tests to measure the impact of our changes, evaluate the effectiveness of our bidding algorithm, or compare different user interfaces. 

Managing these tests previously involved manually setting up A/B tests through code adjustments and executing SQL queries. This process was prone to human error and quite cumbersome. It took a developer in our team up to a week to implement and launch one A/B test. 

To streamline this, we decided to develop a dashboard to create, manage, and monitor these experiments. Our primary requirement was to have a frontend interface intended exclusively for our internal teams, without the need for high levels of interactivity or performance. We aimed to build it as efficiently as possible with our available resources.

Which Framework Did We Choose?

React is the most popular framework for building frontends; we initially considered it due to its vast ecosystem of component libraries and tools. However, we wondered if an SPA framework like React was necessary. 

We also considered alternatives like jQuery or Vanilla JS. However, we ultimately chose htmx for its simplicity, maintainability, and excellent compatibility with Go. This choice allowed us to reduce development time. 

Setting up htmx is also straightforward. You only need to add a script tag to include the dependency – this is a sharp contrast to the complicated build systems common in other frontend technologies. The ability to use a common language across both the frontend and backend was a huge advantage and strongly influenced our decision to choose htmx. 

This led us to discover its advantages, which I will explore further in this article.

How Is Building with htmx Different?

Let’s first review the history of web technologies to better understand the htmx approach to web development.

Multi-Page Applications (MPAs)

Websites were initially primarily multi-page applications (MPAs) that interacted with servers in two main ways. 

The anchor tag first instructed the browser to fetch a new page via an HTTP GET request and replace the current page with the response. HTML forms then permitted data submission to the server using an HTTP POST request, which often redirected users to a new page afterward. 

These interactions were simple and straightforward; the server responded directly with the HTML to be rendered, but also required the entire page to be refreshed with each interaction. Native HTML was also limited to only sending HTTP GET and POST requests. This limitation led to the adoption of JavaScript to enhance user experiences and interactivity.

Single-Page Applications (SPAs)

When it comes to single-page applications (SPAs), server interactions have changed significantly. In SPAs, clicking a button might trigger an HTTP request of any method, and servers typically respond with a JSON object. 

The JavaScript running in the browser uses this JSON to update the state of a domain model maintained internally. This model then refreshes the relevant parts of the webpage, allowing for a more interactive user experience.

However, this method requires the client to understand the exact structure of the JSON response and its meaning. The client must recognize which further actions are valid and available, given the data provided. It must know how to render this data to the UI.

Additionally, the client needs to be updated whenever there are changes to the API. While the SPA approach enables a much more interactive and richer user experience, it comes at the cost of increased complexity.

Hypermedia-Driven Applications (HDAs)

Hypermedia-driven applications built with htmx, on the other hand, extend the capabilities of HTML while preserving the simplicity of the MPA approach. 

htmx generalizes the capabilities of standard HTML, enabling any HTML element to initiate HTTP requests of any method through simple attributes. The requests can also be triggered in response to a variety of user interactions beyond traditional mouse clicks and submit events. Critically, htmx facilitates updating only specific parts of a webpage – targeting and replacing subsets of the HTML DOM.

Since rendering occurs on the server, the client does not require additional logic or knowledge about which states are valid for which entities. The server directly returns the valid state along with interactions that are available in that state of the system, and the browser renders it. 

With these features, HDAs maintain the straightforward architecture of traditional web technologies while facilitating the creation of interactive user experiences like those found in modern SPAs. 

graphic of htmx and how it has worked since 2004
Source: htmx.org

How Does the Dashboard Work?

The core feature of our dashboard was an experiment creation wizard. This wizard enabled users to create and modify various attributes of an experiment using various forms. A typical recurring pattern that we used can be seen below.

<form hx-put={ fmt.Sprintf("/experiments/%s/information", experiment.ID) } hx-target-error="#notifications" hx-swap="innerHTML">
   // Input fields
   <button>Next</button>
</form>
<div id="notifications"></div>
screenshot of what htmx dashboard looks like in the browser

Here, we utilized the hx-put attribute to instruct htmx to send an HTTP PUT request to the specified URL. This carried form data within the request body. 

We also used the hx-target and hx-target-error attributes to manage the server’s response. If the server returns a success status, the response HTML replaces the content specified in hx-target. 

For error responses, such as a 500 status code, the HTML updates the area defined by hx-target-error, which is convenient for displaying validation errors to the user. The hx-swap attribute defined how the response would replace the target. Options included appending to the target or replacing its inner HTML, among other modes.

In the example above, we did not use an hx-target because, upon successful form submission, we redirected the user to the next page of the form using the “HX-Redirect” header in the response.

We also found htmx to be effective for building the group creation page. This is a key part of our dashboard that manages the distribution of different treatments among user groups. This page featured an interactive table that enabled users to easily add, update, or delete rows.

templ GroupsTable(experiment ui.Experiment) {
   <div>
      <label>Experiment Groups</label>
      <ul>Column headers</ul>
      <ul id="groups">
         for _, group := range experiment.Groups {
            @GroupRow(group, editable)
         }
      </ul>
         <button hx-post={ fmt.Sprintf("/experiments/%s/groups", experiment.ID) } hx-target="#groups" hx-swap="beforeend">Add</button>
   </div>
}


templ GroupRow(group ui.Group, editable bool) {
   <li>
      // Editable Columns
         <button hx-delete={ fmt.Sprintf("/experiments/%s/groups/%s", group.ExperimentID, group.ID) } hx-target="closest li" hx-swap="outerHTML">Delete</button>
   </li>
}
screenshot of what experiment update interface looks like in browser

The GroupsTable component, as seen here, is part of a form that uses htmx to enhance functionality, similar to the pattern described earlier. The “Add” button within the table initiates a POST request, which returns the HTML for a new group row. 

This new row is then appended to the end of the list, thanks to the hx-swap=”beforeend” attribute. This instructs htmx to add the response HTML at the table’s end.

Each row in the table includes a “Delete” button that, when activated, does not return any HTML. Instead, the htmx attribute hx-target specifies that the closest li element — the row itself — should be removed. 

This functionality demonstrates htmx’s capability to target HTML elements dynamically using not just CSS selectors like the id tag, but also selectors such as closest, next, and previous. This setup showcases the simplicity and effectiveness of creating an interactive table with htmx, eliminating the need for additional JavaScript. 

Using just a few patterns like these, we successfully implemented the entire experiment management dashboard without any JavaScript. We also successfully deployed it thanks to htmx’s simplicity. It proved to be a perfect fit for our requirements. 

Choosing htmx allowed us to deliver a more convenient and reliable way to manage our experiments. This resulted in tighter iteration loops and quicker product improvements – whether for the UI that users interact with while watching in-app ads or the algorithms used for bidding on ad inventory.

When Should You Use htmx?

htmx is well-suited for scenarios where:

  • your application requires minimal to moderate interactivity
  • offline functionality is not a requirement
  • the core value of your application is derived from backend logic and server-side validation
  • the UI state does not require frequent updates

Its ability to deliver server-driven interactivity with minimal client-side code makes it a versatile and effective solution for building rich user experiences.

Of course, you can use htmx for some parts of your application while also integrating it with more interactive frameworks like React for other parts that demand greater interactivity. Choosing the right tool for the job is essential, and htmx offers a great option for web development where speed and simplicity are priorities.

Senior Product Manager (f/m/d)

  • adjoe
  • Programmatic Supply
  • Full-time
adjoe is a leading mobile ad platform developing cutting-edge advertising and monetization solutions that take its app partners’ business to the next level. Part of the applike group ecosystem, adjoe is home to an advanced tech stack, powerful financial backing from Bertelsmann, and a highly motivated workforce to be reckoned with.

Meet Your Team: WAVE Supply Services
In this competitive adtech market, adjoe stands for greater transparency and fairness for app publishers and advertisers – and a more relevant and enjoyable experience for users.
It’s exactly for this that adjoe has built its own programmatic mobile ad platform WAVE which connects app publishers with advertisers. We are working with dozens of advertising networks, measurement providers and other external services with whom we exchange millions of data points every minute. The WAVE Supply Services team is responsible for developing tools through which app publishers can manage their WAVE integration, analyze their ad monetisation performance and assess the ads’ UX through dashboards and APIs.

Join our discussions, explore implementation, and put your problem-solving skills to the test in our cross-functional Programmatic team!

As a part of the WAVE Supply Services team, you’ll be responsible for developing the face of the product: services to set up an SDK, analyze ad revenue and apps’ UX – from gathering the necessary data from our SDK to visualizing it on the dashboard or providing it via APIs.
What You Will Do
  • Build and manage a cross-functional team together with a Tech Lead: do hiring, feedback exchange, organize routines in the team.
  • Conduct competitors research, market research, customer research to come up with business requirements for the product.
  • Work with designers to prepare mockups for the UI of the product.
  • Prepare and communicate requirements to the engineers.
  • Take care of critical product documentation about features and business logic (both internal and external).
  • Work with statistics from different sources (internal system dashboards, BI tools, spreadsheets) on a regular basis to understand product use cases and identify issues.
  • Align with the company’s overall strategic goals. Work on long-term product roadmap and quarterly OKRs.
  • Align with other tech teams on common guidelines for development, design, etc.
  • Who You Are
  • You have a degree in information technology, economics, analytics, or a similar field or 5 years working experience in product or analyst positions.
  • You have 2+ years’ of experience working as Product Lead / Manager / Owner – preferably in B2B SaaS products, developing web applications.
  • You have experience hiring people, doing regular 1-1s, creating career plans.
  • You can speak both tech & business languages: discuss feature implementation with engineers and business needs with business development colleagues.
  • You have experience in basic data analysis and extracting valuable insights from data.
  • You have experience in working with task-tracking tools (Jira, GitLab, etc).
  • Plus: You have experience in using BI tools (QuickSight, Tableau, MS PowerBI).
  • Plus: You know how to work with SQL, Python, or R.
  • Plus: You experience managing mobile app/SDK development.
  • Heard of Our Perks?
  • Tech Package: Create game-changing technologies and work with the newest technologies out there.
  • Wealth building: virtual stock options for all our regular employees.
  • Work-Life Package: 2 remote days per week, 30 vacation days, 3 weeks per year of remote work, flexible working hours, dog-friendly kick-ass office in the center of the city.
  • Relocation Package: Visa & legal support, relocation bonus, reimbursement of German Classes costs and more.
  • Happy Belly Package: Monthly company lunch, tons of free snacks and drinks, free breakfast & fresh delicious pastries every Monday
  • Physical & Mental Health Package: In-house gym with personal trainer, various classes like Yoga with expert teachers.
  • Activity Package: Regular team and company events, hackathons.
  • Education Package: Opportunities to boost your professional development with courses and trainings directly connected to your career goals 
  • Free of charge access to our EAP (Employee Assistance Program) which is a counseling service designed to support your mental health and well-being.
  • Skip writing cover letters. Tell us about your most passionate personal project, your desired salary and your earliest possible start date. We are looking forward to your application!We welcome applications from people who will contribute to the diversity of our company.

    We’re programmed to succeed

    See vacancies