In the previous lesson, we constructed a decision rubric to guide your migration from React to HTMX. This framework helps you classify any React component into one of three targets: a full server-rendered page, an HTMX-driven partial, or a client-only component. Now, it's time to move from theory to practice.
This lesson will focus on applying that rubric to a real-world, complex feature you're very familiar with: a data table with sorting, filtering, and pagination. By deconstructing a typical React implementation of this feature, you will learn how to map its pieces to the hypermedia architecture. Our goal is to translate a single, stateful React component tree into a cohesive set of server endpoints and HTML fragments orchestrated by HTMX. This process is the core of your migration playbook.

Case Study: Deconstructing a React Data Table
Let's consider a feature common in the CRUD applications you've built: a data table for managing a list of projects. This is not a simple list; it's a rich, interactive component.

As a senior developer, you would likely structure this in React using a clear component hierarchy. Let's outline a plausible structure:
ProjectsPage: The top-level route component from React Router.DataTableContainer: The "smart" component. It holds the state for filters, sorting, and the current page. It's responsible for fetching data, likely using a library like React Query or SWR to manage API requests, caching, and loading/error states.FilterControls: A set of inputs (e.g., text search, status dropdown) that update the state inDataTableContainer.DataTable: The main table structure (<table>). It receives the data array and column definitions as props.TableHeader: Renders<thead>with<th>cells. These are often clickable to trigger sorting.DataRow: Renders a<tr>for a single project. It might include "dumb" presentational sub-components like aStatusBadge. It also contains action buttons (e.g., Edit, Delete).
PaginationControls: "Previous" and "Next" buttons, and maybe a page number display/input, to control which slice of data is shown.
Our task is to take this component tree and apply the rubric from our last lesson to each piece.
Applying the Migration Rubric
We'll work through the component tree, applying our two key questions:
- Does this component's functionality depend on server state?
- What is the required interactivity level and complexity?
1. ProjectsPage and DataTableContainer
- React Role: Manages the overall page route and orchestrates all data fetching and state management for the table (filters, sorting, pagination). This is the stateful heart of the feature.
- Rubric Analysis:
- Server State? Yes. Its entire purpose is to fetch and manage a representation of the
projectscollection from your database. The filter, sort, and page parameters are just inputs to a server-side query. - Interactivity? Medium. It re-fetches data based on user actions.
- Server State? Yes. Its entire purpose is to fetch and manage a representation of the
- Migration Target: Server-Rendered Page and HTMX Partial.
- The
ProjectsPagecomponent maps to a primary Express route (e.g.,app.get('/projects', ...)) that renders a full HTML document (projects.ejs). This document will contain the initial state of the entire data table. - The data-fetching and re-rendering logic previously in
DataTableContainermoves to a server endpoint that returns just the table's HTML. Critically, this can be the same Express route, which checks for theHX-Requestheader to decide whether to return the full page or just the partial.
- The
2. FilterControls (Search, Dropdowns)
- React Role: Controlled components whose state (
searchTerm,filterStatus) is lifted up toDataTableContainer. A change triggers a re-fetch. - Rubric Analysis:
- Server State? Yes. The state of these controls directly translates into query parameters for the server.
- Interactivity? Medium. Changes trigger a server request.
- Migration Target: Part of an HTMX Partial's trigger mechanism.
- These controls will become standard HTML
<form>elements. The<input>for search and<select>for filtering will havenameattributes corresponding to the server's expected query parameters. - The form itself will use
hx-getto point to our/projectsendpoint,hx-targetto specify that only the table should be replaced, andhx-triggerto automatically submit the form on input changes (with a debounce).
- These controls will become standard HTML
The video "You don't need a frontend framework" by Andrew Schmelyun provides an excellent walkthrough of this exact pattern. It shows how a simple form can be progressively enhanced to filter a table without a full page reload, first with vanilla JS and then simplified further with HTMX.
You don't need a frontend framework
This video demonstrates the core pattern of converting React's controlled inputs into a server-driven form with HTMX.
First, watch the segment from this point where the presenter refactors a simple HTML form to use vanilla JavaScript with fetch() to reload only the table content. This is conceptually what React is doing under the hood. Then, watch the following section from this transition, where he replaces that vanilla JS with a few HTMX attributes on the form to achieve the same result with far less code. This is the exact pattern we'll apply to our FilterControls.
3. DataTable, TableHeader, DataRow
- React Role: Renders the data.
TableHeaderhandles sort clicks, andDataRowcontains action buttons (e.g., delete). - Rubric Analysis:
- Server State? Yes. The content is a direct render of server data. Actions like sorting and deleting are mutations of server state.
- Interactivity? Medium. Clicks trigger server requests.
- Migration Target: HTMX Partial.
- The
<table>element (or just its<tbody>) becomes the target for swaps (e.g.,<table id="projects-table">...</table>). - Sorting: Each
<th>will contain an<a>tag or<button>with anhx-getattribute that includes the sort parameters (e.g.,hx-get="/projects?sort=name&dir=asc"). It will target#projects-table. - Deleting: The delete button within a
DataRowwill usehx-deletepointing to an endpoint like/projects/123. Crucially, it will usehx-target="closest tr"andhx-swap="outerHTML"to remove the entire table row from the DOM on a successful response.
- The
The article "Advanced Data Table with HTMX" provides a superb, detailed implementation of these very patterns.
Advanced Data Table with HTMX - Benoit Averty
This article provides concrete HTML examples for implementing the features we are discussing. It's a practical blueprint for building the HTMX version of our data table.
Please read the following sections to see the code-level implementation: Focus on the section Deleting an Element. This shows the exact hx-delete, hx-target, and hx-swap pattern for removing a row. Next, read through Pagination / Sorting / Filtering. This explains how to use a single form to manage the state of all controls and how clickable links in headers can be used for sorting while maintaining the filter/page state.
4. PaginationControls
- React Role: Renders "Next/Previous" buttons. Clicks update the
pagestate inDataTableContainer, triggering a re-fetch. - Rubric Analysis:
- Server State? Yes. The page number is a server query parameter.
- Interactivity? Medium. Clicks trigger a server request.
- Migration Target: Part of the HTMX Partial ecosystem.
- These will be simple
<a>tags or<button>elements withhx-getattributes pointing to the next/previous page URL (e.g.,hx-get="/projects?page=3&..."). They will target the#projects-table. The server is responsible for rendering these links with the correct URLs based on the current state.
- These will be simple
5. StatusBadge
- React Role: A "dumb" presentational component that just displays a value.
- Rubric Analysis:
- Server State? No. It has no state of its own. It's just a visual representation of a prop.
- Interactivity? None.
- Migration Target: Absorbed into Parent HTML.
- This component doesn't migrate; it dissolves. The logic to render a green pill for "On Track" or a red one for "At Risk" will exist directly within the EJS template for the table row (
_row.ejs). It's just conditional HTML.
- This component doesn't migrate; it dissolves. The logic to render a green pill for "On Track" or a red one for "At Risk" will exist directly within the EJS template for the table row (
Synthesizing the New Hypermedia Architecture
By applying the rubric, we've transformed the React component tree into a server-centric architecture:
| React Component | Migration Target | Implementation Notes |
|---|---|---|
ProjectsPage | Server-Rendered Page | An Express route GET /projects serves a full EJS template. |
DataTableContainer | Logic moves to Server | The state management and data fetching logic now lives inside the GET /projects route handler. |
FilterControls | HTML Form with HTMX | A <form> wraps the search/filter inputs. hx-get, hx-target, and hx-trigger handle updates. |
DataTable | HTMX Partial Target | A <div id="projects-table"> or similar element that gets replaced by HTMX swaps. |
TableHeader (Sort) | HTMX-enhanced Links | <a> tags with hx-get and hx-target to re-fetch and swap the table with new sorting. |
DataRow (Delete) | HTMX-enhanced Button | A <button> with hx-delete, hx-target="closest tr", and hx-swap="outerHTML". |
PaginationControls | HTMX-enhanced Links | <a> tags with hx-get to fetch a different page and swap the table content. |
StatusBadge | Dissolved into Template | Becomes conditional logic within the server-side row partial (_row.ejs). |
Instead of a single, complex client-side state object, the application state now primarily lives in two places:
- The Server: The database holds the true state. The Express route handler is the single source of truth for querying and rendering that state.
- The URL: The filter, sort, and pagination state is encoded in the URL's query parameters. HTMX's
hx-push-url="true"attribute makes this seamless, preserving shareability and browser history.
This approach aligns perfectly with the hypermedia principles discussed in the "htmx in 2026" article, where it notes that for CRUD interfaces, the htmx version is dramatically simpler than the equivalent React implementation involving state management, data fetching hooks, and mutation logic.
Conclusion
In this lesson, we put our migration rubric to the test. We successfully deconstructed a complex, stateful React data table and mapped its constituent parts to a new, simpler architecture based on server-rendered HTML fragments.
Key Takeaways:
- A single, "smart" React container component often maps to a server-side route handler that serves both full pages and partial fragments.
- Interactive elements that manipulate server state (filters, sorters, paginators, delete buttons) become simple HTML elements enhanced with
hx-*attributes. - The complex client-side state management of React is largely replaced by the server's business logic and the state encoded in the URL.
- "Dumb" presentational components often don't have a direct equivalent; they are simply absorbed into the structure of the server-side templates.
You have now moved from understanding the "what" and "why" of the migration rubric to the "how." You have a clear mental model for dissecting your existing applications.
In the next lesson, we will formalize this process. You will learn how to draft a component-by-component migration plan for a small React application, creating a concrete document that maps each component to its target implementation and the specific server endpoints it will require.
Can't find a good explanation? Sign up and we'll make it for you
Sign up