Why another framework
I started UiWizard, not with the primary goal of creating a web framework, but because I kept running into limitations with existing technologies.
In general, what I expect from a framework includes:
- Handle all of the use-cases i come to expect from API servers like flask, django and FastAPI
- Auth
- Cookies
- Headers
- etc
- Customization on ui elements
- Dynamic pages that can change depending on the request
- Full CSS and js support (if needed)
Streamlit
To provide some context, I work in the field of machine learning, which often requires building visually appealing interfaces for the models we create. This is where Streamlit comes into play. It's a great framework when the goal is a quick and dirty application—it gets the job done. However, as soon as you try to scale or add complexity, it starts to fall short.
I prefer the request-response model when working with web applications, but Streamlit doesn’t officially support this. To access request headers, for example, you have to dig into its internal API Github discussions.
Another frustration is setting up authentication. It feels like a hack to implement it directly within Streamlit. A common workaround is to set up OAuth at the load balancer level, which is fine if everything is already in place, but not ideal for custom setups.
At some point, I wanted to change how multi-page applications were handled in Streamlit. After struggling with its complexities and limitations, I decided it wasn’t the right tool for me. I even tried to build the project from source and work my way from there. While I appreciate it for quick prototypes, that's about as far as it goes for me.
Gradio
My second approach was going with Gradio. Gradio is the main interface for the hugging face platform.
My initial impression of Gradio was quite good but at the time of assessment Gradio did not support dynamic rendering of pages. This is a recent feature that was in pre-release 5 months ago Gradio pre-release, so Gradio didn’t meet my needs at that time as I started development over 10 months ago.
Reflex
The next web framework I tried was Reflex. Reflex has a lot going for it but some of the architecture of it did not sit well with me. It works by compiling the front end down to a next.js app that is started along side of the python web server. It uses state on the server as the source of truth, which is great but the rx.state classes feels like react bleeding into the python server.
Everything in Reflex is state-driven, including authentication and authorization, which most frameworks handle with middleware.
Styling is done via class parameters, but I would prefer to use traditional CSS or TailwindCSS.
Communication between the frontend and backend happens via WebSockets, which has caused issues for me in the past when the application is behind a load balancer. The browser thinks the connection is live, but there’s a breakdown between the load balancer and the server.
I also wasn’t a fan of how the user interface is structured. For example:
def index():
return rx.hstack(
rx.link(
rx.avatar(src=GithubState.profile_image),
href=GithubState.url,
),
rx.input(
placeholder="Your Github username",
on_blur=GithubState.set_profile,
),
)
If I wanted to nest the link
and input in another container, I’d have to change the structure of hstack
, which feels cumbersome. Ultimately, I wasn’t satisfied with Reflex.
NiceGUI
NiceGUI was by far the best framework I encountered. It checks almost all my boxes: it supports JavaScript, CSS, and is essentially a FastAPI wrapper, so I can use all my existing tools. I have access to requests and responses, and I can define custom response headers!
NiceGUI uses a single server to handle everything, and I was initially very impressed. It has a rich set of components, and the documentation is excellent. If you haven’t tried it, you should.
However, after deeper development, I ran into issues. NiceGUI sets up a WebSocket connection, sending a JSON blob of the UI to be interpreted by JavaScript into Vue.js components. While this works well in terms of JavaScript integration, it comes with a performance cost. When the application loads, it first loads the NiceGUI JavaScript, then sets up the WebSocket connection before rendering the application. This can cause brief flashes of white or black while waiting for the server response, similar to how single-page applications work.
You can even experience this on NiceGUI’s own website when navigating between pages. It flashes briefly as the JavaScript waits for a response.
NiceGUI does provide an example of building a single-page application, but I ran into my usual WebSocket issues again. If I left the page idle for too long, the WebSocket connection appeared to work, but no data was returned from the server. This may have been an issue with my Azure hosting setup, but it was frustrating nonetheless.
Conclusion
I really wanted NiceGUI to be my go-to framework, but after hitting WebSocket issues again, I gave up. That's when I learned about HTMX and realized, "You know what? I can build this myself."
Is UiWizard perfect? No, it probably has more issues than most frameworks, but it’s improving every day, and I’m learning a lot about the web along the way. I’m starting to enjoy it again! And the docs could really use a nice upgrade!