Welcome back. In our last lesson, we contrasted the update mechanisms of React and HTMX, establishing the core difference between client-side virtual DOM reconciliation and HTMX's server-driven HTML swapping. This distinction is the foundation for everything that follows.
Today, we'll build on that foundation to develop a practical framework for your migration goal. You'll learn how to analyze the UI of a React application and determine which features are a natural fit for HTMX and which require the assistance of lightweight, client-side JavaScript. Mastering this classification is the first and most critical step in formulating a sound migration strategy for your CRUD projects.
The Two Kinds of State: Server vs. Client
As an experienced React developer, you're accustomed to managing application state on the client. Whether using useState, useReducer, or a global state manager like Redux, the React model centralizes state within the browser's JavaScript environment.
The hypermedia approach forces a crucial distinction that is often blurred in SPA development: the difference between Server State and Client State.
- Server State: This is the authoritative data for your application, the "source of truth" that resides in your database. It includes user accounts, product lists, article content—anything that needs to be persisted and shared. In a React app, this state is fetched from an API and then duplicated on the client.
- Client State: This is ephemeral, transient state related purely to the user's immediate interaction with the interface. It answers questions like: "Is this dropdown menu currently open?", "Is this modal visible?", or "Is the 'show password' checkbox ticked?". This state doesn't belong on the server, as it has no long-term significance.
The fundamental rule for migrating to an HTMX-centric architecture is this: let the server manage server state, and use minimal JavaScript to manage client state. HTMX becomes your primary tool for interacting with server state, while a small library like Alpine.js (or even vanilla JavaScript) becomes your tool for client state.
Mapping UI Features to the Right Tool
With this state distinction in mind, we can begin to categorize the features common in the CRUD applications you plan to migrate.
When to Use HTMX: Interacting with Server State
Any feature that requires reading from or writing to your database is a prime candidate for HTMX. HTMX is essentially a declarative way to orchestrate interactions with server state.
Common examples include:
- Loading data: Fetching an initial list of items.
- Pagination: Clicking "Next" or "Previous" to load another slice of data.
- Search & Filtering: Submitting a query that returns a filtered set of results.
- Form Submissions: Creating or updating a record in the database.
- Deleting Data: Removing a record.
In each of these cases, the user's action needs to change the server state, and the UI must then reflect that change. HTMX excels at this by making a request and swapping the server's HTML response directly into the page.
The video below provides a compelling real-world example, explaining why they chose HTMX over React for a shopping cart feature. The cart's contents are classic server state, and the UI needs to update in multiple places—a perfect job for the hypermedia approach.
React vs HTMX: Why we chose HTMX?
This video from Kodaps Academy walks through the decision process for rebuilding a shopping cart.
Watch the segments that cover the problem with client-side storage (The problem), the drawbacks they saw in using React for this specific feature (React's drawbacks), and how HTMX provided a better solution for updating UI based on server state (The HTMX solution). Finally, pay close attention to the conclusion about when HTMX is not suitable (When not to use HTMX), as it sets the stage for our next topic.
When to Use Client-Side JS: Managing Client State
When an interaction is purely cosmetic or doesn't need to inform the server about a change, making a network request is unnecessary overhead. This is where a small, targeted JavaScript tool shines. Alpine.js is a popular companion to HTMX because it shares a similar philosophy: adding behavior directly to your HTML with minimal footprint.
Common examples include:
- Toggling the visibility of a dropdown menu, modal, or accordion.
- Switching between tabs that are already rendered on the page.
- Providing instant feedback for form inputs (e.g., a character counter or real-time password confirmation matching).
- Interacting with a third-party, browser-only JavaScript library (e.g., a charting or mapping library).
This next video does an excellent job of positioning HTMX and Alpine.js as complementary tools, not competitors, and clarifies their respective domains.
HTMX vs AlpineJS - Which should you use for your web app?
This HAMY LABS video provides a clear breakdown of the roles of HTMX and Alpine.js.
First, watch the introduction explaining why they are complementary tools (Complementary tools). Then, focus on the sections detailing when to use HTMX for server interactivity (When to use HTMX) and when to use Alpine.js for client-only interactivity (When to use Alpine).
A Practical Decision Rubric
The following article from the CommCare HQ Style Guide provides an exceptionally clear set of rules for making this decision. Although the guide discusses migrating from Knockout.js, the principles are perfectly transferable to your React-to-HTMX migration context.
CommCare HQ Style Guide (Bootstrap 5)
This guide offers a mature, battle-tested mental model for dividing responsibilities between the server (with HTMX) and the client (with Alpine.js).
First, read the section Update Your Mental Model to understand the philosophical shift. Then, review the "Typical use cases" row in the Concept mapping table. Finally, and most importantly, study the rules of thumb in the section When to use Alpine vs. HTMX.
Based on these resources, we can distill the decision-making process into a simple framework. When you look at a UI feature, ask yourself:
- Does this interaction require knowledge from the database, or does it need to permanently change data in the database?
- Yes: This is a job for HTMX. Your server should handle the logic and return the new HTML state.
- Is this interaction purely for local presentation or immediate feedback, with no need for the server to be involved?
- Yes: This is a job for Alpine.js or vanilla JavaScript. Keep it on the client to avoid a needless network roundtrip.
The Division of Labor in Practice
Let's solidify this with a concrete table, adapted from another excellent tutorial. This provides a clear, at-a-glance reference for a typical CRUD application.
htmx and Alpine.js: Build Interactive Web Apps Without Heavy JavaScript Frameworks
This tutorial includes a perfect summary of the division of labor between HTMX and Alpine.js.
Focus on the table in the section titled Understanding the htmx + Alpine.js Division. Also, review the sections on When NOT to Use htmx and the concluding summary of what the combination is powerful for (Conclusion).
Here is that key table for your reference:
| Concern | Tool | Why |
|---|---|---|
| Data fetching (lists, items) | htmx | Server owns the data, returns HTML. |
| Form submission | htmx | Server validates and returns the updated UI. |
| Search/filter | htmx | Server filters the data and returns an HTML fragment. |
| Toggle element visibility | Alpine.js | Pure UI state; no server roundtrip is needed. |
| Instant form validation UI | Alpine.js | Provides immediate feedback without a network call. |
| Dropdown menus, modals, tabs | Alpine.js | These are local, transient interactions. |
| Animations & transitions | CSS + Alpine.js | x-transition can manage UI state for animations. |
This clear separation of concerns is one of the most appealing aspects of this stack. HTMX handles the communication with the server-state backbone, while Alpine.js provides the fine-grained, client-state interactivity that creates a modern user experience.
Conclusion
You now have a robust mental model for dissecting a React application and classifying its UI features for migration.
- Server State (HTMX): Features that rely on the database as the source of truth—such as loading, creating, updating, deleting, searching, or paginating data—map cleanly to HTMX attributes like
hx-getandhx-post. - Client State (Alpine.js/JS): Features that manage transient UI state—like modals, dropdowns, and tabs—are best handled by a lightweight client-side tool to avoid unnecessary network latency.
This ability to distinguish between server and client state is the strategic lens through which you will plan your migration. You're no longer thinking of a monolithic client-side application but rather a server-rendered application progressively enhanced with two distinct types of interactivity.
In our next lesson, we will complete this high-level overview by enumerating the key trade-offs of this migration, weighing factors like bundle size, SEO, developer complexity, and latency. This will equip you with a complete strategic picture before we begin diving into the code.
Can't find a good explanation? Sign up and we'll make it for you
Sign up