Large table pattern using htmx
So this is kinda unrelated to anything else, but I just wanted to put out something that is a really useful pattern, that I found using HTMX that just works really, really well, that I've ended up using a bunch of times. And I was just programming something, building something for a client, yesterday. And it just made me think that this is something I've done it enough times now that, you know, I do think of it as a pattern. So it is basically like a large editable table, with a lot of information in that table. So, you know, you could think of it I'll just I'm gonna give specifics because it's kind of like I may as well what's what's the you know, trying to come up with some example or something is harder than just saying what I was actually working on.
Lazarus:So I'm working on a database of candidates for, elections. So, you know, a candidate has a bunch of information that could be personal information, campaign information, contact information, you know, there's a lot that goes into it. Also, history of elections, you know, bio, there could be paragraphs of stuff written, there could be websites, social media. So just imagine it's like if you were to put it just into a straight table, you know, it might be 70 columns or something like that of information. So, you know, relatively relatively big, data set for each candidate.
Lazarus:And then we have elections. And so, you know, every election is gonna happen in a district, and it's got a date. So a date, a district. Eventually, it's gonna have results. It's gonna have some other stuff.
Lazarus:But I you know what? Let's just focus on the the candidates for now because what I'm thinking is, you know, basically just this large table of candidates, is something that has been really, useful to use HTMX for. So first of all, let's say let's say you have a whole bunch of candidates, and, you know, maybe we'll start at just like 200. We can actually let's go higher. So just because to show that why this is such a good fit for HTMX.
Lazarus:So let's say there's 500 candidates. Right? So if you try to show a table with 500 with everything, you know, that's going to take a little bit to load. It's going to take a little bit to show. So you probably just want to show and and also it's just too much information at once.
Lazarus:You know, it's not going to fit left to right on the screen. There's a lot of issues with kind of trying to show everything. But if you're sitting down to actually, you know, look through this large data set of candidates, and I'm actually building editing tools, so I'm building, you know, an admin admin tools. So it's more than just how you display the data. You know, I'm not trying to make it look in a certain sort of visually appealing.
Lazarus:I'm I'm basically trying to get the most efficient for somebody who has to work with this data, figure out what's going on, make updates, and save, and, you know, basically understand everything, be able to find everything they need. So I don't actually want to use pagination. That's step 1, and I've been kind of moving away from pagination a lot. Now that I have tools like HTMX because you don't when you try to fit something, like, let's say even just 500, which is actually not that many, onto a screen. If you're using a virtual dom, if you're using, react or vue or even something like, you know, alpine, you don't have to sort of make everything a virtual dom, but, actually, I won't include alpine in that because that's not really the purpose of it.
Lazarus:But if you're using 1 of these things, nextjs, they will always recommend, and in fact like insist, that you don't show that many on the screen at once. You you do sort of smart loading, you can do like infinite scroll. There's a lot of things you can do and you can do that with h tmx a 100%. You know, no reason you can't. But, I want to just when you go to the page you everything loads.
Lazarus:And it's actually, if you're not using a virtual dom, if you're just using html and what the browser already has, it's not a problem to load that many. So there there's no technical hurdle for loading that many, it's just in terms of what the user wants. And the thing about this is that the user, in my experience, often does want to see a bunch on the screen at once, especially if they have to do work. They just want to stay on that 1 screen. They use command f, you know, to find something.
Lazarus:You can build a special filtering search or whatever, but the browser already has your search. Right? And I use it all the time and so do the people that I I build stuff for. So they have a large text area, you know, not text area, the input, but they have a large, you know, table full of all this information. So each row has a limited amount of the data.
Lazarus:Right? So now we're starting to get into how it's actually, you know, why what we're using h tmx for. So each row, we choose our most important fields for that candidate. You know, party name, district, whatever else. And then we have a little show details.
Lazarus:And and we're using a table, so this is like TRTD. You could use, you know, flex and tailwind and stuff like that, but I like using the the table sometimes because it's just so reliable that everything's going to be lined up, which is nice. So we have a little show details button and this is our, well the first place where htmx is going to come in here. When you show details that has an hx get to okay, so actually I'm going to step back again. When we build our table and we're building our templates, 1 of the things that makes this a pattern is just the way we structure that.
Lazarus:So we'll have like an index dot I use blade, so I'm from the Laravel world, so I use blade, but whatever your templating language is probably has the same, you know, you you divide these these, snippets of HTML into, slightly different, you know, into different files, into different places so that you can include them as needed. Right. So blade lets you do this. So so I have my index dot blade dot php and that's where I'm going to put my basic stuff about the page and also my table tag saying, okay, my table's going to start here. Then within that I do my for each for each candidate.
Lazarus:Right? And then within my for each it doesn't include to my candidate row. So each 1 of those rows, each candidate, and then we pass the candidate, pass the candidate variable, candidate model, to that row. So all the row needs is which candidate the row is for. Right?
Lazarus:That makes sense. So now in our snippet for the candidate we have the row, the t r, and the starting t r tag, the ending t r tag, and then we fill in all the candidate information we need and we have that little show details. Now we don't actually have the show we don't have the details loaded because we want to lazy load those to make the whole thing load faster. We don't need to show all that HTMX for this. We fetch it from the server when we're ready.
Lazarus:We don't need to preload everything. We can if we want to, but we don't need to because, a little load like that is fast to the server. So our show details is gonna have an h x get to slash candidate slash the ID slash details. And that snippet just takes the candidate with that ID and spits out the HTML. That's another template.
Lazarus:So we have 3 now. We have the index, we have the candidate row, and we have the candidate details. Those are our 3 little snippets. And the candidate row and the candidate details each take a single candidate object, symbol single candidate model. So we have the show details button.
Lazarus:When we click that it's an h x get to, you know, the target is just the same details thing that was clicked. You know, you can set a specific target if you want to load it into somewhere or you can just have it replace the show details button, which is what I did this time. And, you know, you do an outer HTML swap, so the show details button disappears. Whatever you get back from the server, which is just, you know, this is gonna be a fast call because you're just getting the information for 1 candidate. That's literally 1 database call or maybe a couple if you've got some related fields and stuff like that.
Lazarus:But you click that it goes to the server, and it pulls that candidate details template, you know, you pass it the candidate from in the ID from the URL, pulls that candidate template, the details template, and puts it into your table in, you know, whatever your your your, cell for that is. And I like to put make that cell, you know, nice and big. Another possibility here is that you can add another row and then make it, you know, td call span 100 so that it kind of pops the details up below everything else. But actually I think I think the layout is nice if you if you can just keep it inside a detail cell that is mostly empty until you click the show details. And then it fills in that whole cell, but it's sort of separated off to the side so that, you know, it's everything maybe to the right is all your details.
Lazarus:And and you can sort of design those however you want. Maybe you have a photo. Maybe you have, you know, 60 fields of information, a couple paragraphs of bio, whatever that is. It can be as long as you want, but, you're only loading it when you need it. So so this is sort of the pattern, you know, there's, the full so this is this is the start of the pattern because right now this is just to show stuff.
Lazarus:So you have those 3 things. You have the index with everything, you have each row is its own, its own template, its own little, snippet, and then within that the details is its own little snippet. So 1 of the benefits of this, if you want to edit a row, you can have an edit button. That edit button will replace the t r. And you can do this with, an h x target equals closest t r.
Lazarus:So, you know, you you set up your edit form, so now we're adding another template, the edit template, you set up that form, and you set the h x get to, you know, slash candidate slash your candidate ID slash edit, that returns your edit form, and you put the target h x target equals closest tr, and it's going to pop that in, replace your current t r that you're part of, so replace your candidate, row that you're looking at with your edit row. And then if you cancel it you can have a, it just goes back, you have another route to get that single, t r, that single row, that single candidate row, you pass that candidate row, and it will just put back the same candidate row that you had. So that's the cancel. And then if you save it, you know, takes all that that form from your edit form and then spits out that candidate row. So you just have a couple templates, you know, and these are very logical templates too.
Lazarus:This is how you might organize your code anyway, and it's what all it's doing is using that pattern to kind of let you swap stuff in so that you're just working in this could be a gigantic list. But you're saving things, you're editing things, you're loading details, you're closing the details, you know, how you close the details could be another 1, but I think just all you really need to do is have a button inside your show details. When you bring those details in have a hide details button. And all that does is the same thing that the cancel button did from the edit, which is just reload that t r into the closest, you know, closest t r, and it goes and fetches that candidate of whichever row it is, and puts that candidate row into your t r and replaces it. So this is all just, like, you're just using these same tools over and over again, but the the effect is really good.
Lazarus:Effect is really powerful. You have this gigantic list, but you're seeing only what you need to, You can edit anything in it. You'd never have to leave your context. And the last thing I'll add here is adding things to it. So, you know, for this particular thing, I've got all these candidates.
Lazarus:When you want to add a candidate, I just put basically it's like the edit form but, you know, it's a new create new candidate form at the bottom of the table. And it just looks like the other rows of the table, but it's like an add button, it has an add button instead of, you know, instead of all the the information about the candidate. So you fill in whatever information you want to. Maybe some of it's required, maybe some of it's not required. And when you save that, so this is just another row in the table, or it can be outside the table.
Lazarus:Maybe your form you want to look a little bit different. So let's let's move the form outside of the table just for the purposes of this. But when you save it, you're doing, you know, you have your form that you that you use to create this new candidate and it's using htmx2. You do an hx post, you include, you know, your CRSF token. It it's your form just like any regular form.
Lazarus:But you add to it, an h x on, and h x on, h x on request you know, I'm blanking what it is, but it's like h x on request complete, or HTMX request complete, or something like that. I forget what it was, but I'll I'll try and include that in the in the show notes. But basically it's the the, HTMX event that happens once the form is submitted and goes through and the response comes back. And then you check-in there if the response was success, then you reset the form. Right?
Lazarus:So your form submits using h x post to your, you know, slash candidates route just like it would if it were a normal form, but you use h x post and use HTMX so that it submits the form and then it clears it out on success. So what happens to the return stuff? So you return your h x target is going to be the table. So you give your table an ID, and then you can use, I think it's after end, or before let's see, after begin, after end, after begin. Yeah, after end.
Lazarus:So you have a, you know, your table is made up of t r's, and it's actually the the t body. So you need to use the t body as the target because if you use table as the target it actually the child is t body, so then it's confusing. But if you use t body as a table, your your so you can use any CSS selector as your target for where to put the data. So when you create this new 1, it's going to create it, in the database and then send you back the new candidate row. So you got to figure out what do you do with this candidate row?
Lazarus:So you can just put it at the end of your table for now. Right? So so you choose your your table t body, and then you do, after end, and which is gonna take all the children of t body, which is all the t r's, and it's gonna put that new t r in at the end. So when you save, it's gonna clear your form, and then it's gonna add that directly to the table, at the end. And this is just like, you know, these all seem small, but it's extremely powerful.
Lazarus:You've got all this stuff happening and it's lightning fast because it's just using the stuff that's in the browser. It's just using HTML. When you click something it loads it. It's always loading something relatively small at the same time. So this is just a pattern.
Lazarus:I just wanted to kind of like lay that out because I've now I'm realizing how many times I've used this pattern. And HTMX has really made this, an easy exercise to do these sort of things. And you can go crazier with if you want to, you know, make it more of a data table with filters at the top, and it's not really going to affect that much, you know. It's just gonna you're gonna reload with some different filters and all the stuff that's loaded is gonna be your HTML. It's gonna have h tmx buttons in it.
Lazarus:They're all gonna work just like before. You don't need to special initialize them or anything like that. You know, it's got these things are all sort of, loaded. They sort of do what they say, you know. It's like you've got these buttons, you've got these actions.
Lazarus:This is that that hate us, The, hypermedia as the engine of your application state. It really is. Like all these actions you can do and all these buttons, you're kind of just loading them in as you need them. Your you get to decide on your server side and with your templates what actions you allow. You know, an edit, a save, a cancel, an add, like all these different things.
Lazarus:And the way that you organize it is very intuitive in in my experience. You know, you would separate your template into each row. That's a good way to think about it. You know, how do you want to make each row look? You're just gonna be looping on 500 of them anyway.
Lazarus:So why not just make that an include and have that be its own template. Then whenever you need to you can load that specific 1 again and place it where you want to. So yeah. I hope this was, interesting to you. It's been kind of, you know, it's, like I said, just something that I've used so many times now.
Lazarus:So if you haven't tried it out yet, I would say give it a shot. See if, all that stuff I just said makes any sense. I don't really know how audio coding through audio really works for people. I haven't listened to a lot of stuff like this where it's like just someone talking about how they would code something. But I don't know.
Lazarus:Maybe it works for you, maybe not. But, yeah. That's the sort of pattern and hope you're having a good day.