Frappe UI 1.0 Release Frappe UI 1.0 has been released, four years after the frappe-ui v0 npm package was first published, according to the project's release announcement. The toolkit now ships 100+ components, automatic dark mode, backend data utilities, and AI readiness, and consolidates its API to 727 props across all components using only 188 unique prop names. The release follows six months of work started in March to stabilize the library, which now supports close to 25 Frappe apps including Studio, Gameplan, Drive, Helpdesk, Insights, Builder, HR, CRM, Learning, Mail, Wiki, Writer, and Calendar. Frappe UI 1.0 is out. It's been four years since frappe-ui v0 was published on npm. I should have done this sooner, but better late than never. It has 100+ components, automatic dark mode, real world examples, full screen recipes, utilities that connect to backend data, and it is AI ready. An all-in-one toolkit for building modern Frappe frontends. The rest of this post is about how it got here, because a lot of decisions went into it and I want to explain them. How it started Frappe UI started as a components folder in the Frappe Books project. Button, Avatar, Badge, and a few more. When I moved to Frappe Cloud in 2020, I copied the folder over, like any naive engineer. Then in 2022 I was starting Gameplan and did not want to copy paste again. So I extracted it into an npm package. Fast forward four years and we are on version 1. After Frappe Cloud we started churning out apps. Gameplan, Drive, Helpdesk, Insights, Builder, HR, CRM, Learning, Mail, Studio, Wiki, Writer, Calendar, and it has not stopped. We are close to 25 apps now, excluding the tiny ones. It is very hard to build a UI library that has to work for all of them. Why version one, why now v0 was a license to break things. I am never happy with an API. There is always one more thing to add, one name that could be better, one prop that means two things. When there were three apps this was fine. With 25 apps it was not. Studio is built on top of Frappe UI, so every time a release changed a component, Studio's source had to change too. Other apps got hit too. And since everyone started building with agents, the number of apps depending on Frappe UI is going up faster than before. The team asked for a stable version. So in March I started working on version one seriously. I thought it would take a few months. It took six. Good components, bad library Frappe UI v0 was built by me and a few teammates over four years. Each component looked fine on its own. When I looked at all of them together, they did not fit. Switch had a toggle event. That makes sense, you toggle a switch. But you almost always use a Switch next to other inputs, and all other inputs have a change event. Also, if you are using a v-for loop you wont have to make an exception for Switch. So the right design is change for Switch too. Every chart had its own click event. datapoint-click on BarChart, slice-click on DonutChart, stage-click on FunnelChart, cell-click , link-click , point-click . Six names for one behaviour. Dialog, Popover and BottomSheet each had a prop for "the user cannot close this by accident". disableOutsideClickToClose , hideOnBlur , swipeDownToClose . Very explicit. But there can be many ways to trigger a behaviour, escape, click outside, swipe down, and the behaviour is the same. Dismissing the thing. Button shipped with icon-left and icon-right slots. Now they are prefix and suffix , and those names are shared across every component that has something before or after its content. TimePicker had minTime and maxTime . DatePicker had minDate and maxDate . Rating had rating from . Each one made sense alone. Side by side, cutting all of them down to min and max made more sense. I am not blaming anyone for the old names. Some of them were mine. It is hard to design a good API one component at a time. You fix Dialog in one month and Popover six months later and you never see both together. The number I am most happy about from all this: Frappe UI now has 727 props across all components, and only 188 unique prop names. The number of names did not grow with the number of props. Everyone wanted one more Combobox prop A lot of the API in 1.0 came from looking at what apps did when I couldn't think of them myself. Combobox is the one example. You type something, the item is not in the list, and you expect to create it. v0 did not have this. So out of 25 apps, every one that needed it invented its own. Frappe Cloud added allowCustomValue . Someone added creatable . Insights added allowCreate and a @createOption event. Someone else made options accept a function that returns a promise. I refused to build any of those, because in my mind "create" meant a different thing each time. In CRM, creating a contact means opening a new contact dialog. In the same app, creating a lost reason means creating it inline and refreshing the list. There is no one implementation that works for every app. And if I had listened to all the requests, Combobox would have multi , searchable , creatable and clearable as booleans, which is sixteen combinations I would have to test, and four props we could never remove. So I did not add the prop. I added a way to put custom options in the list. js