FastUI: Build Better UIs Faster(github.com) |
FastUI: Build Better UIs Faster(github.com) |
Please tell me this isn’t common. It’s been a solved problem for at least 15 years now. At least since PHP had any form of prominence on the backend in the late 90s early 2000s. At a minimum going all the way back to knockout.js, handlebars, YUI and ember.
If this is common in 2024 I have lots of questions
I can't wait to be handed an application by my data scientists that uses this and is a complete trash fire that I need to rebuild from scratch... Again. I guess streamlit has competition.
It allows data scientists to showcase the benefit of their model as a full experience.
I wouldn't use it for a large scale app. But tools like these are excellent for quickly mocking up a sample UI to sell an idea to your leadership. Too often leadership does not get the benefits of a ML/AI/DataScience driven feature if they can't see it inside something that resembles a UX.
Front end people don't get this pain, because their work has immediate visible impact. Backend work doesn't need to be sold because it naturally emerges out of the needs of supporting a certain number of users. ML work, lacks visibility just like backend but isn't as self evident as backend work.
Tools like streamlit fill that gap.
The focus of these products should be 0 to 1 rather than 1 to Inf.
I know a lot of backend developers who are backend developers specifically because they don't enjoy frontend work and prefer to leave that work to people who actually enjoy it. And I know a lot of frontend developers who feel the same way, only from the other side.
The tagline of FastUI is "Build Better UIs Faster," but I think this will likely end up being "Build Passable UIs Faster," because yes, for simple cases, you can represent HTML components as Python or Javascript or whatever other language you want, but once you need to build something complex on the frontend, it becomes very irritating very fast when you have to work like this -- and good luck getting your frontend buddies to help you, because now they have to get up to speed on a new framework to understand what it's doing.
To each their own but typescript is one of the best programming languages I’ve used. I do enjoy python a lot but whenever I use it I feel like I’m going a decade into the past, especially with their tooling (lint, types, formatters, package manager, venv, etc).
I wrote a post recently about how I feel like this loathing of all things FE/JS is overblown: https://bower.sh/front-end-complexity
I am the author of typedload, a similar library written in pure python.
Mostly for fun I started to see how much I could improve performances, benchmarking against other libraries.
I was very surprised to find out that pydantic2, despite the rewrite in rust, wasn't overwhelmingly faster… in fact it is still slower than my pure python library in some cases.
But, I guess having "fast" in the name sounds good!
https://ltworf.github.io/typedload/performance.html (I re-run them every time I release).
But, unlike frontend devs, designers and product managers believe these complex things are needed, they almost never are and actually bring a lot of issues with them instead. I rarely see a complex component/combination (in a SaaS, let alone on some landing/public page) and think, "well, that adds real value over just a standard component". If you can point some out, please do; there are very obvious cases where something else than standard is needed, but that also depends on your definition of standard; if I can buy complex off the shelve somewhere, you are not building something complex in the frontend and so you can use simple means to hook them up without the complexity.
One common front end component that you can’t build with vanilla HTML is an autocomplete/typeahead input. I’ve had to build a few of these in JavaScript and I do think they’re genuinely helpful. It’s helpful to get suggested options as you’re typing. I also think search results that get filtered in real time as you type are helpful and can’t be accomplished with pure HTML.
https://github.com/pydantic/FastUI/blob/main/demo/forms.py#L...
This looks like a good thing. In that regard. Maybe not for us, but definitely a very useful tool for all developers because it can potentially let you deliver business value rapid.
Which is what I read from the lines you quote. I see no backend vs frontend discussion here, I only see a useful tool.
Even if you want to eventually build it into your React frontend, a tool like this will offer immense value for internal testing by users who won’t be able to do it through something like Postman, which is basically 99% of the employees in non-tech enterprise.
Then we built a set of generic frontend components for that in React once.
And we then had an instant UI for 50+ models, but the frontend devs could trivially selectively override both the whole page for models where the default was too simplistic, or how to render specific types of fields or for specific fields of specific models.
The "instant UI" was flexible enough that for most "backend" views we never customised it on the frontend. But being able to have the frontend devs customise the user visible things was essential, and thanks to having the ability to replace things bit by bit they could focus on the things that didn't look or work right, instead of "everything". And a lot of their work then made it back into augmenting the backend with info to let the generic frontend apply those improved views elsewhere as well.
I have only had a cursory look at FastUI. If they do something similar to that, then great. If they actually try to have the backend generate all or most of the UI and serve that up instead of serving up metadata, then I fully agree with your issues.
Agree completely that is the only sane goal - just wondering how you got there?
Especially for business oriented apps, most of the models only need a list view with a table, and a detail view displaying some properties and related lists of models.
It works wonderfully.
For better or worse, we lost this capability in the path for code purity, RESTful services and SPAs.
It is even behind rails g scaffold when it comes to RAD.
We then let designers/frontend devs override that on a case by case basis instead of building from scratch, and where possible tried to roll that into the generic UI as components triggered by specific metadata from the backend. It was not entirely agnostic, but it was largely driven by metadata from the backend, so e.g. schema changes rarely needed much, if any, UI work unless they fundamentally changed how the page worked.
Edit - someone who has built with this has commented that they found things snappier than using Streamlit
I've had quite vocal arguments from front-end devs which, when we stripped back what they were actually complaining about, came down to the front end being in service of the back end, rather than vice versa as they were used to.
A really good form is still a PITA. I've been trying to perfect it for years but haven't found anything to sync frontend and backend validation that doesn't suck/involve a lot of boilerplate.
I think there's a fundamental verbosity to the way most SPAs do data management - I've seen it said elsewhere that when you build a SPA, you basically end up having to reinvent the database, by which it is meant that an inordinate amount of your code is dedicated to extracting data from the backend via an API and then keeping track of that state using mountains of front end code.
At some point, you have to decide - is this really two separate applications (a backend and a frontend), or is it actually just one? If you don't have a need for multiple different clients, and you already have a backend written in Python (or some other backend language, e.g. Elixir, Kotlin), then maybe you can get away with writing a whole lot less code if you can find a framework that fills in that whole in-between layer for you and lets you stick with the language you already have.
When you jump into the world of single-page applications, things get complex pretty quickly, because the use case for needing an SPA pushes the web app into a full desktop application.
Ultimately, for a highly interactive and dynamic "desktop-class" user experience, there is added complexity. I think that's why so much movement within the FE world has moved away from "SPA for everything" and into these mixed dynamic apps. Islands, React Server Components, NextJS, they all help create a middleground between a document-based website with no dynamic elements with a full blown desktop app experience. They all have real tradeoffs, in particular adding an entirely new backend service to serve the front end.
For many projects, react + react-query is probably enough.
Having said that, my argument from https://bower.sh/dogma-of-restful-api still stands: when you build an API that is RESTful (1:1 mapping between endpoint and entity) you are unknowingly pushing the complexity of data synchronization to the FE, which requires a well thought out ETL pipeline.
This probably doesn't help my case but I've been building a simplified middle-layer for react to bridge the gap between react-query and full blown SPA: https://starfx.bower.sh
But that's just my own conspiracy. I have no idea. Perhaps things changed since I last ran the benchmarks.
Benchmarking isn't easy. I know that my software using my library mostly loads Unions, so that's a focus in my benchmarks. However I have absolutely no idea of what the other users are doing with it, and my benchmarks might not be relevant to them.
In the end I wrote them for myself mostly, to check for possible regressions in performances.
Then it'd just apply that recursively. For a simple model there'd only be two levels: The view, containing a bunch of columns, and the columns themselves, and the view type knew to iterate over the columns in the view and look up their type and tell them to render themselves. For more complex views frontend devs might e.g. provide a custom view type that'd group columns based on certain attributes, but still delegate to the generic mechanism to render those columns.
So replacing a part just meant writing a component that knew how to render itself from its props, just like any other component. Just that some of those props would be coming from the backend.
We'd generally try to avoid replacing components if the only reason for a component was to override how something was rendered, and to focus on why it should be rendered differently and consider if we could reflect that 'why' in the data, or other ways.
E.g. were certain fields grouped together because they were semantically related? Then reflect that with an attribute in the "meta schema" for those columns, and make the frontend component render related groups according to the type of that group (let's say a bunch of fields reflects a user address). Incidentally this tended to be very simple, as with a relational model when there were groups like that the right thing was often for them to be a separate table/model anyway, and we 'just' needed to distinguish between type handlers for rendering an object 'as the page' vs. 'as a reference' vs. 'as a group within the page', and that was easy enough to distinguish "well enough".
At a top level, we'd look for a registered view-level component (or fall back on the default), within a view if the backend returned the model instance "in-line" we rendered it as a group, and if it returned an id, we'd render it as a reference.
So a "User" object for example would render as a profile view if it was not your user and you went to a page where the associated API call returned the User as the top level model, you personalised profile page w/optional edit view if it was your user, or a "pill" linking to the user profile if it was just a reference.
The idea was that frontend components should focus on how to render specific types of data in a specific context, rather than specifying how specifically to render the page, because it'd make no sense to have the frontend e.g. try to render a field that no longer exists, or try to render a field as a different type it is when the meta data about which fields were available and what type they are was already available.
I maintain an open source library in my spare time for free, that you are welcome to ignore if you find better alternatives.
My comment is my own opinion, and a quick search for your name and pydantic backs it up. It’s just boring to read the same thing, every time.
1.Json schema/AJV return not very human friendly results and they're hard to associate with a specific field to display the error nicely 2. If you want to validation as the user is typing to clear errors as they're satisfied, you have to run the entire schema, not just for that one field 3. Any async validations like checking if a username is unique is harder if not impossible with this approach
<pre>Error: {JSON.stringify(mutation.error.data.zodError, null, 2)}</pre>
Formik (https://formik.org/docs/api/errormessage) does OK with the "touched" and "errors" objects, that helps you render out the error messages nicely, next to the inputs, but for my very first use-case I wanted to validate a File input and it seems to struggle with that a bit. Can't do it without suppressing some TS errors but maybe it's an edge case. And it of course doesn't handle the server half of the equation. So.. yeah.In a way I disagree with you.
On accessibility: I see more custom components that nobody cares to make it properly working with keyboard interaction compared to native HTML input components.
On standards - mixed feelings: we had XHTML, now we don’t. We had IE and jQuery to deal with it, now we don’t need it, but I still see many places that have the old “works best with” message, now with Chrome instead.
On maintainable code: it’s a different shit show, we exchanged spaghetti code for billions of npm dependencies that break if you dare to try and upgrade something.
Remember how you could just "add" a database connection to your project, plop a data source on a form, then a datagrid or a bunch of text/check/comboboxes and the standard control, wire it all up in a couple clicks, and things just magically worked?
That was a lovely time to work on any kind of line-of-business app.
You can do these in both Delphi and Lazarus, except with a much better GUI framework (comparing GUI only here, not everything you'd get with .NET). If anything having worked with Windows Forms for a while i think it was abandoned before it reached even what you could do in the midlate 90s in Delphi.
You would be even more impressed with Delphi, because after connecting your data source, your form fills with data at design-time. Lazarus and Typhon does the same.
And if I remember correctly, it could also do the "fill the form with data" trick.
Layouts >> fixed size window
By now C# is a much more powerful language, though.
A template language (that isn't Python) is sufficient. Even better if you can render the templates from different languages.
I'll never be defining web app presentation in Python.
You might want to try (my project) iommi https://docs.iommi.rocks/ It's very different.
With FastUI, you write Python.
Usually we've been prototyping with streamlit, but found that at times to be clunky. FastUI still has rough edges, but we made it work for our lightweight app. Note: we generally found it snappier than streamlit.
That said, I’ve found it to be clunky in a few ways:
1. State management is not intuitive — anything that involves rendering complex state/reacting at it changes requires a ton of session state type stuff.
2. I’ve very often had to go into css/markdown hacks
3. The control flow of when it’s running/executing makes for clunky interactions
4. Some API decisions are painful. For instance, a block str comment in the file will render as st.write
5. Long running (or even not instant tasks) make for some annoying engineering challenges — the UI is tied to the compute, so you have to be smart about juggling it
6. Caching is not smart — it’s quite useful, but it’s easy to get lost in caching/state
I’ve been dreaming about streamlit but a little more state/function oriented. E.g. react-based. Not far from fastUI
These are both great tools for prototyping, but I’ve found that I’ll pull up tailwind + some UI framework when I want something slick and it’ll take a few times as long.
I don't know why someone would say "Build UIs" and not mention html, it seems pretty myopic to think a UI automatically means a web page.
Fastui seems prettty barebones mostly a form adapter to pydantic models that I can see.
Works beautifully and fast, send only rendered code to the front. Have db admin for when you scale
> htmx
Htmx went 1.0 a little over three years ago. Is it really something you can call old fashioned?
I'm not even really fan of Python and I really miss static typing (renaming things is painful) but oh well, I'm so productive with it that I can live with this.
Honestly, I think it's the only framework that I've ever used (never tried RoR though) which allows total flexibility while never bothering you with technical thoughts.
It's still as shitty to deploy as it was a decade ago but now we have containers so who cares.
I'm writing this app during a pause in my career (mainly frontend) after a burnout and it's reigniting my love for writing web apps.
The holy grail for me would be a Django backend that renders react on the server. A Django nextjs.
Still not really sure how the two compare, but there was just something about the extremely tried and battletested code. Nothing got in my way, the code told you what it did, it was fast etc.
"Pydantic models for UI components" seems odd to me. Surely runtime validation is unnecessary in this application and static analysis is a better fit. I am guessing it's the close integration with FastAPI that really holds the value here. For me, ideally this wouldn't use Pydantic but given the author and their situation, using Pydantic is probably the main reason this project actually exists.
Hold on I thought HN is all hyped about HTMX now? Isn't that what that does too?
1. It can save a lot of repetitive boilerplate code
2. You can keep validation rules in one spot.
I've been working with some toolkits like these after years of 'backend api, frontend vue/react/angular' and it definitely can save a lot of time. It's not always the best fit, but there isn't one approach to all applications that is the best fit. Everything has tradeoffs.
FastX is popular these days.
Do the features offered by these really warrant the complexity?
The syntax looks very awkward.
IMV, XML is the best format for any Declarative UI. Tools to generate that XML (or HTML) are better value than these systems.
I would love to see an XML based UI generator in Python.
When you've stopped screaming in horror at what you've suffered, we can discuss whether you still think that's a good idea.
The overall concept is good, but sadly xslt is constrained enough to be massively painful for this kind of thing.
I'm far from being one of those people that's like all in on AI, but I've definitely been super impressed with its abilities to generate basic boilerplate code when you're working on project from scratch.
Projects like FastUI allow maintenance of a project with fewer skills, directly reducing the cost of maintenance: owning teams spend less time learning and maintaining their skills and there are fewer bugs due to engineers making mistakes because of low skill-level in a technology.
Also, projects like FastUI make the project's tech stack much simpler. Simplicity helps reliability, incident resolution, security, testing, adding features, removing dead code, refactoring, etc. Highly-productive teams apply these ideas and keep their systems as simple as possible. As tools like FastUI mature, they will become standard in healthy engineering orgs.
EDIT: my main issue is figuring out how to simply deploy a website.
Maybe someone will even recommend Cloudflare Pages or a $5 VPS.
But for that use-case, isn't it easier to use something visual and established like Retool (https://retool.com/) or that generates nice react code, like MUI Toolpad (https://mui.com/toolpad/)?
Seriously, I first tried it in 2013, used it for work from 2014-2016 and here and there for odd projects later, and the amount of churn and complexity of the ecosystem has been surprising every time I check back on it.
My Obj-C from even longer ago pretty much all runs fine and is fairly understandable even for most iOS devs who started after Swift was the default.
Tainting what used to be a lean frontend library with server side concepts [1] was a net negative in my opinion.
I'm building something like this for mobile: https://www.applin.dev
Which are basically just server side routes / rendering + optional pre-rendering ala static site generators using simplified react components?
/s (but only half)
To those dissing it for production use case over a hand build frontend by a frontend dev: yeah, no shit. But not everything needs such work put in. This is perfect for internal tools, and there's plenty of indie hackers out there proving that you don't need an immaculate UI to get customers anyway. Use it for what it is good for.
We need more tools like this, because it cuts the development effort in half when it hits its stride. That's very valuable for internal tools especially, where you need something functional first and pretty second.
That being said, as a biased Laravel developer, I can highly recommend Filament both as an admin panel builder and declarative UI component kit like FastUI. It's Livewire based instead of React/Vue/etc and so firmly within the Laravel ecosystem, if that bothers you. https://filamentphp.com/
Yet Filament manages to be fairly 'pretty' out of the box. I did a POC for a client project, and replicated about 10% of their current system in about a month, and got a fair amount of praise from folks at both the professional look/style/feel, yet... I did 0 on the styling, just used the defaults.
Yes, Filament has some rough edges when you try to color outside the lines, as with any toolkit. However, I don't think I've found anything else that comes remotely close to the combination of productivity and decent styling out of the box. Not just in PHP, but in any stack.
---
> This
Yeah, this uses Python and is not specifically an SSG, so it makes sense for a server to be needed. Making an SSG that uses this would probably be possible, though.
> Remix
Remix is a fullstack app framework with a focus on data flow, directly going against the JAMStack current. Makes sense that it needs a backend server.
> Next
NextJS offers static site generation support. https://nextjs.org/docs/pages/building-your-application/rend...
> Astro
Astro started out as an SSG, and even now that server rendering is supported first-class, static site generation is the default and on-demand rendering needs to be opted into by the developer. https://docs.astro.build/en/basics/rendering-modes/#server-o...
Developers always find ways to down-compensate gains due to Moore's law and its infrastructure equivalents.
It looks like Flet is for client-side code. It lets you write Flutter apps with Python instead of Dart.
> Simple Architecture - No more complex architecture with JavaScript frontend, REST API backend, database, cache, etc. With Flet you just write a monolith stateful app in Python only and get multi-user, realtime Single-Page Application (SPA). -- https://flet.dev
If I'm writing Python that runs on the mobile device, it must talk to a server to read & write data. Doesn't this still require an API backend, database, cache, etc?
> It looks like Flet is for client-side code.
For web you can package Flet app to client-side (with pyodide, all Python logic runs in the browser, see an example here: https://gallery.flet.dev/todo/) and run as a server-side app (or server-driven) with Python logic running on the server (example: https://flet-controls-gallery.fly.dev/layout - notice faster loading compared to client-side one).
> If I'm writing Python that runs on the mobile device, it must talk to a server to read & write data. Doesn't this still require an API backend, database, cache, etc?
That's correct. Any backend service which provides Python API can be used when running Flet app on a mobile: FastAPI, Firebase, Supabase, Pocketbase, etc, but you use Python to call that which is awesome especially for beginner and non-web developers.
Thanks for your work on it, and (shameless nudge!) I'm really looking forward to iOS camera access, and easy iOS deployment!
It's kind of unbalanced, as web tech, on the other hand, is pretty good at building desktop apps.
The fact that you found it so simple coming from another framework speaks loudly about your previous one. But they do compare in that Django is the easy one to start where most choices are already done for you.
There really should be a max bill on every cloud service. I've accidently ran up a bill with AWS. Alarms aren't enough. Notifications aren't enough.
Performance exceeded what was available at the time, scaled super well, but not something I’d ever go back to.
- xslt is not a programming language. As soon as you start using xslt:functions you’re doomed.
- it is not a fit for interactive UIs. It’s good for static content, trying to mix in js goes wrong very quickly.
My opinion is that these days infra is cheap enough that you get all the same benefits by doing standard templating in your language of choice from your data in json or whatever. Which I know is essentially the same thing without the specialist software and dsl. And that’s kind of the point.
Unfortunately no browser has ever supported anything past XSLT 1.0.
I'm a full stack guy myself, so I'll stick with React.
The top results I get are for the Fast and Furious movie. After specifying the context ("API", "web", "framework") I get hits for remote desktop software which also has API:
https://www.starnet.com/fastx/ https://www.starnet.com/help/fastx-v3-api/
And "fastx python" returns a module for parsing FASTA files
FastHTTP, FastGPT, FastChat, FastAI, FastLED, FastText, FastClick, FastHub, FastSAM
Most of these have 10s of thousands of stars on github.
Learning Phoenix (and a few other libraries like Ecto and now LiveView) is what would eat up your time.
Phoenix used to be very easy for Rails/Django/Laravel devs to pick up a few years ago, but now that LiveView is thoroughly built-in and Phoenix.View has been replaced by Phoenix.Component, it's much less familiar to those learners.
If you decide to give it a try and have trouble, please email me (email in profile) or drop by the Discord (https://discord.gg/yMGCamUMnS).
The Python version is a port from its R stablemate (well, maybe more than a port: a re-engineering). If it's as solid as its R parent then it'll be a very useful addition to the Python UI landscape.
Not every web app needs to survive HN levels of traffic. Empowering the <insert profession here> that already knows Python to make an app to automate their team’s toil is a great thing.
I say this with absolute conviction; you're just deferring costs, technical debts with compounding interest. When you come to pay it off, it'll bankrupt you.
Jinja:
{% for item in items %}<br> {{ item }}<br> {% endfor %}
Blade:
@foreach(var item in items)<br> {<br>@item<br> }
The real comparison point is client rendered SPAs, which is the status quo in web app development that SSR frameworks are competing with. If that's your starting point, SSR is a strict optimization and a significant improvement for almost all usecases besides internal admin tools that are only meant to be used on desktop.
Fast to run? Well.. no.. it's Python.
I personally care much more about development speed and time to market/customer than execution speed. It's good enough for 99% of cases.
I am at the ORMs are bad club, but if you try any of the systems that encode the logic paradigm into their interface, they are a completely different kind of beast. (MS has brought some of it into C# with linq so you can use with the Entity Framework. It's not a fully logic system, and has so many attrition points with the Entity Framework that it's mostly not useful at all.)
1. keeping a log of queries per request with stack traces
2. at the end, checking if there are N+1 problems
3. print a big warning with example SQL and stack trace to the console
During dev if I miss a prefetch_related/select_related the console will start to print gigantic stack traces telling me where the problem is. I don't have these problems in production because I get alerted to them in dev.
These are not only solvable problems but EASILY solvable problems!
(Also, in iommi tables, most of the times it can automatically figure out and do the prefetch/select related for you, further diminishing the problem)
var customersWithOrderDetail = context.Customers.Include("Orders").ToList();
Would generate :
SELECT * FROM Customers JOIN Orders ON Customers.Id = Orders.CustomerId
The awareness is the key imo. That's what iommi's tool gives you out of the box.
select * from book where author_id = 5
If you represent your data as objects, you'll create a Book class with an attribute of type Author. If you now want to run the query above, you'd say (in c# likelihood, but not a real Entity query):
Database.Set<Book>().filter(author.id = 5).all()
But that instructs the ORM to fetch the author attribute from all books, and only then filter it by id. So, you will end up running the following query:
select * from book join author on book.author_id = author.id where author.id = 5
There is no reasonable way to represent the difference between this query and the one on top with an object representation. And even though both have exactly the same semantics, databases have been historically bad at optimizing them so the second one can be orders of magnitude slower.
You grab all Albums. Then you make a table of the albums with name and artist name. The artist name is in a separate table linked with a foreign key.
So the code might be:
for album in Album.objects.all():
print(album.name, album.artist.name)
Django will need to query from the foreign key ID (album.artist_id) into the Artist table to get the name of the artist. This means every loop step does a query.So "N+1" because it's N queries (every album) plus 1 (the original "all albums" query).
The fix in Django is to do `Album.objects.all().select_related('artist')`. Then there will be just one query.
Anyway, I disagree. Sometimes the conversion of raw data from the db to Python objects can be the bottleneck for a view. Of course, almost always when this happens it's not really a problem per se as the view is plenty fast enough.
Have you benchmarked this?