How we dropped Vue for Gleam and Lustre
Nestful is now exclusively powered by Gleam and Lustre. Vue was completely removed and TypeScript is only used for glue code and FFI.
It took a while, but as of today, if you visit Nestful you are going to be served by Gleam — a friendly, type-safe, functional language compiling to Erlang and JavaScript — and Lustre — its front end, Elm-like framework.
This is the conclusion of a transition that started more than a year ago, stemming from my frustration with TypeScript and the state of front-end frameworks. Gleam and Lustre were and still are a breath of fresh air and I am glad to have made the move.
This post will detail how those early motivations measure up in hindsight, and what it means (and feels) to maintain tens of thousands of lines of Gleam code.
You could do it too
Nestful is what I call “almost done” software. It’s not quite the completeness that are SQLite or Sublime Text, but is also not as ever-changing as something like Windows, a bastion of agile development these days. This positioning makes Nestful able to survive such a rewrite.
Nestful is valuable to users as is, and so they are more tolerant of features not being a part of every browser refresh. Some are happy with the low amount of jarring changes. Not every app has this luxury, with some pushed by their market segment to chase competition. Thankfully Nestful is a very opinionated tool in a niche without much innovation, so could easily afford the rewrite.
However, a rewrite is not the only avenue. Since Gleam is not a pure language, it’s very easy to interact with existing JavaScript or TypeScript code. New code and much-needed refactors could be done in Gleam with little to no hassle. In fact, this is how Nestful started, with Vue SFCs written in Gleam, and later with Lustre taking over state management while Vue kept on rendering.
You could do the same in your company, implementing carefully and incrementally, and enjoy the benefits described in the blog post for those new sections of code.
Of joy and trouble
Clearly, Nestful does change. “Almost done” is very different from “done”, with improvements added in a calculated manner. As of writing, the next planned big feature is showing items from third party services. This requires a non-trivial amount of architecture to then be able to arbitrarily add tasks from other services and have them bubble up next to one another for the user to compare priorities.
To do that, the code base needed to support changes maintainability-wise, but also the enjoyment of writing them. The thing is — those are intertwined. Yes, there is an aspect of enjoyment that stems solely from the writing, in isolation. But there’s also the mental load that comes from bad maintainability which kills that joy of coding.
In my original post about rewriting in Gleam, I mentioned some of the language specifics that were a joy — simple, exhaustiveness checks, pattern matching, friendly syntax. Those all turned out to be true. A stark contrast to TypeScript’s complexity, where one has to write two programs; one serving logic and the other types. All I wanted was to make sure there aren’t any null references.
Difficulties on the front end most often boil down to managing state. jQuery closures, Angular services, React and Vue hooks and stores and of course, the resurgence of back-end-centric solutions like Phoenix — those are all attempts trying to make sure incrementing a counter won’t turn on the user’s toaster.
Elm and its architecture almost entirely solved state issues like the aforementioned by getting state soundness in exchange for boilerplate. You write more defining your state model, the actions that can be performed on it, and the side effects they may incur and in return you get significantly fewer bugs and, most importantly, developer clarity of how state flows. That is a fine exchange. Boilerplate is much easier to live with than being ever-vigilant about state racing like it’s Daytona.
Along the years I’ve advised companies on how to replicate this architecture with TypeScript, solving issues like the that “spooky actions at a distance”, unpredictable reactivity, and unruly side effects. I’ve done it with Nestful. However, there was always this feeling of trying to fit a square peg in a round hole. It’s well worth the benefits, but could I have done better?
That’s where Lustre — Gleam’s implementation of The Elm Architecture — shines as the true star of the show.
Explicit state handling and not having to fight off reactivity made Nestful much faster. The architecture also avoids over-design by allowing the developer to easily compose and recompose state and its handlers, Gleam and Lustre do make refactors easy and clean, because in the end, it’s all just regular functions with strong type guarantees.
This all concludes to a single word: fun. Programming Gleam is fun, and I do not intend on going back to the old, cold days of TypeScript if I can help it.
Lessons Learned
The following is a short summary of some lessons learned along the way, in no particular order (except for that first one). I hope that they can be useful to you.
The AI Elephant in the Room
I know I’ll get a lot of replies saying how Gleam is too new for AI and therefore productivity is reduced compared to something like React. Well, I’ve been using Opus 4.5 for some of the rewrite and I can tell you two things:
It is much better at Gleam even compared to Sonnet 4.5 and Codex 5.2
LLMs are very knowledgeable idiots
Given their stupidity as their main weakness and their vast knowledge as their main strength, LLMs need very precise guidance and supervision to crank out what I’d consider acceptable code. In that context, having more knowledge about a specific framework or language results in diminishing returns, especially with tools like Claude Code or OpenCode which give the LLM more information.
Instead, it’s much preferable to guide the LLM to avoid its stupidity. The constraints that are inherent to Gleam and Lustre do that perfectly. Yes, the LLM might try to concatenate a conditional CSS class instead of using attribute.classes, or rename variables weirdly because it has PTSD from JavaScript name collisions. That is much easier to deal with than having it RNG an architecture like we’re playing The Sims.
Forget view hierarchy
This is more of an Elm architecture generality but is useful even if you don’t adopt a framework that follows it. Due to components holding state, React and Vue make it extremely easy to model your state hierarchy identically to your view hierarchy.
This is often not only counterproductive but downright harmful. State should be modeled completely separately from the view. Drawing state encapsulation lines that match view encapsulation will just mean you’ll be ever-busy trying to circumvent your own architecture as you discover state cross-requirements.
Design late
JavaScript and to some extent TypeScript are both landmine languages and therefore incur significant refactor cost, as refactoring doesn’t only mean avoiding pitfalls in the new code, but also breakage that is caused by removing the old code.
This is almost entirely absent in Gleam, which has your back. This makes the drawbacks of designing early — making incorrect assumptions about the future — more harmful than the logic inconsistencies that still can sneak into Gleam code, especially since it’s not pure.
Instead, just build really well for what you have now. The code will tell you when you need to change the design, which will be a mostly painless process.
Accept the boilerplate
Lustre requires you to commit to more boilerplate. It is a fact of its design. Fighting it by being “smart” will come back to bite you down the line.
Consider your FFI boundaries carefully
FFI is by far the most bug-prone area of the code. This is understandable. Be careful when writing it and its glue code to Gleam.
I hope that tooling in the future could detect type inconsistencies between TypeScript types and the types declared in Gleam for the external function.
Have fun
I may be repeating myself — well, I am repeating myself — but remember to have fun. Slight adjustments to the things you do can go a long way in making you enjoy them. Even if you don’t like Gleam or Lustre (how?!) — incorporate what you do like, even if in a small way. I haven’t done my research but I’d bet people who enjoy the work do it more productively.
What more do you need?
