FastHTML vs. Streamlit - The Dashboarding Face Off

Streamlit is the go-to dashboarding solution for showcasing ML models or visualizing data. It has a vibrant community, multiple years of development under its belt, and tons of third-party integrations. On the other hand, everyone that tried to create complex interactions, like drill-downs or logins, knows that control flow can get messy really quick. Initially simple dashboards often evolve into something bigger and the simple-but-powerful Streamlit formula may not always be up to the tasks.

FastHTML is a new contender in the arena of Python web frameworks and, according to its docs, "it excels at building dashboards." FastHTML stands on the shoulders of giants, giving you a smooth Python experience for authoring web pages, while allowing access to the foundations of the web, like CSS and JS, at any time. We will see if FastHTML can put code where its mouth is, by building the same dashboard, step by step, in both frameworks and investigate their strengths and weaknesses.

This is a talk for data enthusiasts that dabble in web technologies for the sake of showcasing their work or building internal tooling. Do not expect a course on building customer-facing web apps. We will build a dashboard that features:

  • an interactive Plotly chart
  • a drill-down with detailed information shown in a second plot
  • a login
  • multiple pages and navigation

We will examine how hard or easy it is to implement each of these features and how interacting with them in the browser feels. At the end we will see if the reigning champion can defend their crown or if the ambitious contender takes the win.

This session took place in track Django & Web and was classified suitable for novice domain / intermediate python by the speaker.

Transcript (auto)

Auto-generated from the recording utilizing Open-Source AI. Speaker labels (Speaker 1, Speaker 2) reflect diarization, not identity. Timestamps refer to the recording.

Speaker 1 [00:07]

Okay, yeah, as you've heard, I'm Tilman. I'm here with DataCitus EXTO. Right now, my official job title would be something of data consultant. So I'm working with our customers to improve their data science processes and their data pipelines. and I want you to mention the following yeah you're a data consultant just like me you're working at a customer for department a of that customer specifically keep that in mind it's going to be important and you just completed your first data project pipelines are running models are humming and now your last task is present what you did to the team of department a how you're going to do that? Are you going to do a PowerPoint? Nah, static, boring. You're a consultant but not that kind of consultant. Are you going to show your command line? No, like there are non-technical folks there, like they're just going to shut off completely if you're showing like command lines. You should do an interactive dashboard. It's bright, it's colorful, it's on the web like everybody loves interactive dashboards. But how are you going to do that? It has to be quick and dirty. Streamlit, obviously. Who has heard of Streamlit? Okay, nice. It's the best in the biz, battle-tested, lots of integrations, you can do all the plotting you want. But you've also heard of this This new framework is really, really fresh. It's fast HTML. Who has heard of fast HTML? Who was confused because it sounds like fast API? Yeah, me too. It sounds really interesting, but you are no web dev. This is a web framework. You never work with Django. Is it for you? Is it for you as a data person who just dabbles in this web stuff? The docs say like fast HTML excels at building dashboards. Today we're going to find out, we're going to split the timeline and see what would have happened if you've chosen Streamlit or if you would have chosen fast HTML. And just a quick disclaimer, I'm not a hater. Like I love StreamNet, I even brought my special StreamNet socks for last year's conference. Fucking limited mobility. So we're going to see how it works out. How are we going to do this? Am I going to show you a PowerPoint? No. Am I going to show you a console? No. I'm going to show you an interactive dashboard. one and I'm just gonna refresh it really quick because resolution changed what you see here is on the left side that's fast HTML and on the right side that's streamlit and here below we've got the code of the app that is running right now so we can see the output and the code of it okay so first thing you have to do is you have to show the team the data that you worked and we can all collectively pretend that this this is not the iris data set some of something interesting actually and we need to load the data for this plot the scatter plot from a blob storage or like iceberg whatever that takes a while and we're going to simulate that in this case with a function that has a big old sleep command in it so I'm not really loading data but it's still enough to see how it will work okay so first let's have a look at stream it this is dream that I'm just going to refresh this here and you see it says running an expensive calculation and there it is this is our plot has highlighting here everything works and it's really really simple so this is all the code that I needed stream it always runs the script from the top so the first thing you have is the title then the text and the plot and every time one of these commands is executed it's immediately reflected on the UI so that at the time that this title here is executed you see it on the UI so you've got this really nice yeah lazy loading thing where you can see something but the script is still plotting and fetching data and stuff like that that's really nice on the other side we have fast HTML and for you to see it how it works I have to refresh the whole site so please focus on the left you see nothing you see nothing you see nothing there it is so fast HMS root based anyone who has seen like a fast API app or Flask app knows how to read this I hope. So we have this root here it's the index root and we can call it with a get command and we are building the website like we're building HTML directly with these fast texts that's what they are called. The thing is because it's root based you only see something when the root returns so this is going to get rendered and then you see something so first drawback for fast HTML here it's a little bit more code and you don't get this nice lazy loading like if it takes a while for your data to load people will just assume your dashboard does not work that's not good so next we're going to see if we can replicate this lazy loading in fast HTML and as you can see here we've got a little spinner and there it is I can just do it again for you to see there it is spinner spinner spinner and there's our plot so that's much better how do we do that so fast HTML is based on htmx which is basically javascript framework where you don't have to write javascript which is always great and the main point is that you can just swap parts of your HTML in and out. So what we do here, instead of directly rendering the plot, we are rendering a placeholder that immediately when it gets loaded by the browser calls the plot root, and the plot root then generates the plot and swaps out the placeholder. And with this area busy, we're not only getting this nice spinner result, But a little bit of accessibility, too, which is also nice. Okay. So, with that, we have feature parity now. Like you see, it looks a little bit different because Streamlit has this Plotly integration and everything. It looks a little bit more coherent. But otherwise, both things do exactly the same. Speaking of integration, as you can see here, with Streamlit, you can just call the Plotly chart function and give it a Plotly chart. It's just throwing it on the UI. With FastHTML, you need a third-party integration package, which is called Plotly to FastHTML, which has the same functionality. You just need to give it the Plotly headers to load Plotly on the client side. and then you have this Plotly to FastHTML function which does exactly the same thing. But integrations are a little bit more bumpy here because Streamlit is around since, I don't know, and FastHTML is around since last June, actually. So the ecosystem is still growing, basically. Okay, you've got your plot, you've got your lazy loading, but actually you wanted interactivity so you want a drill down you want to click something on your first plot and get a second plot that shows some detail okay we're just going to check out how it works so here in fast.html I'm just clicking something and below here I see which point I selected and I have this bogus data that I made up which is like the details of whatever point we selected and we can do the same thing with streamlit and it works quite slowly why is that here on the other side we we have instant reactivity we can click and and there's the plot and every time we click on Streamlit it starts to load again. What is it doing? Streamlit always runs from the top so every time you change something which we're doing here we're getting this event from the Plotly chart and if in the event some point is clicked we are actually computing this other plot but every time we click something it's running from the top and re-computing refreshing the data from our blob storage to get this first plot it doesn't make sense really but this is how Screamit works this is the simplicity of it but in this case because we have this long running function it's really downside so how did we do it on the other side we use this on click callback and just say okay send a post command to the details route and target the details container the details container is here it's again just a placeholder where we can swap in other content later and then we have the details route which takes X and Y value and then creates another placeholder so we can get this lazy loading spinner again we don't really need it here because it's instant but we still do it and then we have this details plot which gets called from the placeholder details to actually swap in the plot so we have a little bit more code again here on this side but through this root system the control flow is still clear And we've got this instant reactivity, because every time we click something here, you can see the selected points changing below, because it doesn't recompute the first part, because why should it? It's not really necessary. But of course, the Streamlit people thought about that, like this is not an uncommon problem. That's why they introduced caching. So what we did here is we wrapped our plot function in stcached data. So each invocation of this function is cached actually. So when we first click on here, it's going to take a while, but now it's instant. Because this plot here is not actually calculated, it's just cached. That's all fine and dandy but as you know there are two hard problems in computer science and one is naming things and the other thing is cache invalidation and I really like that I can give this saying referring to the cache invalidation not to the naming things. This will be a problem when you have some kind of live data because you are now caching the call for user interaction but you do not know is there any new data so when i refresh this page it's still the old data if you have yeah if your data is changing you refresh the fast html page you get the fresh data if you're now refreshing the streamlet page you get the stale data in our case it doesn't matter but it's something to keep in mind you if you're using this caching functionality you need to think about when do we need to invalidate the cache. Okay, now you're ready to present. Your team is impressed, your boss is happy and now he wants this demo to be deployed for everyone in Department A to play around with. But your boss says not Department B because Department B people are jerks damn your department B people so you need a login something to keep department P out okay how do we do that we will just add this sidebar with a nice login interface you can see we replicated it for both of our apps and And we're just going to type in our really complicated password and here's our app. And then we're going to type in our other really complicated password and we're in two. Okay, was that hard? So for Streamlit, here's where it gets complicated. As you can see, the code blew up a bit, and what we want the code to do is basically, if you're not logged in, show this sidebar with the login stuff. Then, if I click log in, collapse the sidebar and show the actual content. How this works is I have to set this session state for this sidebar to be expanded, but I also want to retain this state over multiple reruns of this script. so I need to use this session state here and then set the page config and then with this sidebar I can build the input the login form and this here only runs if I submit the login form and then I have to call ST rerun which is basically a go-to and if anybody of you has heard the talk on exceptions being go-to's, you know why go-to's are not exactly a nice thing. But this is what you end up with. If you want to stop the execution of your notebook somewhere of your script and then go from the top, you have to use this rerun. Because if we log in, we set in the session state this token that we get from the user log in and then we go from the top again and here in the in this state we then read the sidebar state that we set down here to set the page config so the sidebar then collapses and then we can check down here if the token is present and render the actual content did everybody get that it's not that nice to read actually okay how we did we do it on the other side so just ignore this this is some custom CSS to make the sidebar go whoosh yeah this is not really needed actually what we need is this down here so we extracted some components so everybody who has seen like react or something like that knows what this is so these components are just reusable UI elements here so we have our layout here we have the title of our app we have the sidebar here that can contain something and we have the main content here that contains yeah the content so I can call this layout and set the content and the sidebar separately then I have the main content here which is exactly the same thing that was in the route down here and I have the login sidebar stuff which is basically just the login form and down here I have an if if in I have the user in the session which means that the user is logged in I show the main content events if it's not there I'm showing the login page with the login sidebar and then I have this login post method down here which basically does the login and then if it's successful it swaps out the dummy content in the main page with the main content and it swaps out the login form with yeah nothing a placeholder that is just empty and the the rest is the same so just to show it again no no I don't want to save this password actually. We have to go back and then again to get the login. There it is. So this is what happens we have the login and the sidebar and this dummy content then we log in and when I press this button and the login is successful it's going to swap out this content so it's nothing and the bar collapses and it's going to swap in here the main content gets a little bit time to get used to this style of coding but having these routes in my opinion makes it much more readable than this collaboration of if statements and reruns also this this year is really hard to debug and this year you can just open the developer tools in your browser and see which root It gets called and so on so my opinion the authentication side is where stream it starts a breakdown actually Okay You're good to go you deploy your app. Everybody's happy the whole department a They love it. What now your boss comes to you and says here actually I'm getting really old I can't read it anymore. Like can you add a slider to? like make the Scatter plot bigger say yeah, no Sure, let's do it So in your app, we have to log in each time now, unfortunately You have you have a slider here it works just In the sake of time I'm going to speed up a bit It makes stuff bigger and smaller your boss is happy. No, actually he's not happy he says Who puts the slider above the plot? I want my control elements below the plot like a normal person Can you do that and you're like? Yeah, boss, whatever you say so you try that and actually This is really hard and streamed it because as you know things go top-down down so if you want something below something else to influence it like it's not even declared when the plot is rendered so how do we do that in streamlit we have to set the this marker size state before the plot calculation is running and then down here when we define the slider we have to tell it okay your value key in the session state is this so that Streamlit then knows that this value in the session state that is persisted between reruns belongs to this slider below the plot and how to do this is in the Streamlit documentation under advanced topics and I find it confusing that I need to employ advanced techniques to have a slider below something okay how does trust HTML does it like it doesn't change really we take this slider where is it here it's input type range and we just moved it from here to here and nothing else changes so it's like this this was the part where stream that I coding it in streamlit took more time actually than coding it in fast.html and this is like this kind of famous plot where you have two lines and first streamlit is much easier you're much faster but at a certain point fast.html just overtakes it because your interaction gets much more complicated okay now your colleague has a nice data project too and the boss says yeah you know you already have this like dashboard can you just integrate their work into your work and you're like yeah whatever you say so we need multiple pages now and how do we do that in stream that you can do that I have to be really precise to not hit the reload button so in stream that your navigation is always in this sidebar and you can just switch pages here so this is a sub page in fast.html you can actually put it anywhere um so for example here on top and now it's not working nice um okay so you can put it anywhere um and how would you do that in streamlit in streamlit now you have to refactor your whole system to put your old code into functions or into separate modules that can then below here be put into ST pages these ST pages get put into an ST navigation and then you have to run these different navigation thingies depending on if you're authenticated or not so things get even more complicated what do we do in fast HTML and fast HTML you build a navigation which is here it's just a list with like class drop-down so it looks nice and then you have an additional route that is just a subpage so I changed nothing but just added the sub page this is how it should be and not like refactoring the whole project to just add a second page yeah okay because I'm nearly out of time I'm going to skip improved authentication and go over to the conclusion what should you use actually so stream it has the upper hand when it is when time with time is of the essence so it gets you started fast as you saw to get something you like it was five lines of code and fast HTML first needs some boilerplate like setting up all the routes and stuff like that. Streamlit all in all even when it got harder to develop there was less lines of code actually like it was like two times the lines of code in fast.html. In streamlit you have many third-party integrations fast.html is lacking but it's actually getting better. In streamlit the whole page refreshes every time in fast.html you can be much more granular. Streamlit Control flow gets confusing road based control flow of fast HTML in my opinion is much better and one of the Important things I think here is using streamlit teaches streamlit nothing else But using fast HTML 8 actually teaches you web Fundamentals because fast HTML standing on the shoulders of giants. It doesn't reinvent something It's just CSS HTML and a little bit of JavaScript that you hopefully do not not need to write But this is transferable knowledge. You may not be a web developer, but at least you know something Yeah, you know a t-shaped mindset Yeah Okay, so my verdict is if you're gonna throw it away if you need to do it in a day Stream it everything else fast HTML

Speaker 2 [26:10]

Thank you for the talk. Thank you for the earworm. I'm going to repeat that before I go to sleep today. We do have a few questions. First of all, let's start with what is this black magic where you compare it to web apps or web code below?

Speaker 1 [26:23]

It's fast HTML like it's it's it's just a fast HTML web app that starts another fast HTML web app as a subprocess and the streamlet app as a subprocess and then shows it in an iframe and basically reads the The code of the file and just displays it and the switching is basically just get Python in the background Checking out another commit and then reloading the page. It's nothing else

Speaker 2 [26:55]

All right, can I easily extend FastAPI with FastHTML?

Speaker 1 [26:58]

No, there are not compatible. They are both Based on starlet. So they have the same base, but they're yeah, it's it's not really compatible you can actually use htmx one of the base components of fast HTML with With fast API but like the developer experience is a little bit smoother and fast HTML. So So this is the difference. FastAPI is for APIs that return JSON or something, and FastHTML is for returning web apps with HTML responses.

Speaker 2 [27:35]

Would you recommend fast HTML for more sophisticated apps maybe even customer facing or just internal ones? Yes

Speaker 1 [27:42]

Definitely at data silos. We're building a tool right now For internal use, but maybe we're going to sell it. We are not sure yet but this is like medium complexity we have at the moment like two or three users, but it's going to scale and It has a lot of interactivity And we build it in fast HTML and we have yet to hit the wall where complexity gets unbearable

Speaker 2 [28:08]

Right. Potentially last question. Among the many web frameworks that were the main reasons to use FastHTML or Streamlit, did you also consider other frameworks such as Dash?

Speaker 1 [28:20]

To be honest, no. The main reason that I chose fast HTML was because I wanted to try it out and I read a lot of HTMX and people were hyped about HTMX and fast HTML gave me a really convenient way to use it from Python. So, yeah. This is not a scientific comparison. This is pretty biased.

Speaker 2 [28:49]

All right. And with that, we have to wrap. We're out of time. Thank you again to our amazing speaker.

Tilman Krokotsch

About — in the speaker's own words

I'm a data scientist, machine learning engineer, AI developer, or whatever else you want to call it. After finishing my PhD I am now working as a consultant at Dataciders ixto, where I'm helping our customers to never make wrong decisions again.

Social card for talk: FastHTML vs. Streamlit - The Dashboarding Face Off