Legacy-proof UI: Part 6 — From Figma to React, Vue, Svelte, Preact, and beyond
Table of Contents
Over the course of these articles, I am reflecting on why UI code is much more prone to become legacy as opposed to other types of software. As I identify the reasons behind this phenomenon, I propose an approach — not the most obvious one, but, as I will do my best to justify, is key to opening the door to making UIs legacy-proof and scalable as well as offering extra perks.
- Part 1 — Why does code become legacy?: By using a simple metaphor, I try to illustrate the problem and use it as a reference for the entire series
- Part 2 — Why is UI development legacy-prone?: Another illustration that builds on top of the previous one, further clarifying the problem
- Part 3 — How to make UIs testable and easy to change?: A concise survey of known approaches to UI in the context of the problem outlined in the previous articles and where they fall short as well as proposing a new solution
- Part 4 — React as a decoupled stateless view in Storybook: In this article, we will consider an example of what it means to have a stateless view separate from business logic.
- Part 5 — MVU architecture in a React application: Example of how to make a completely stateless view interactive
- Part 6 — From Figma to React, Vue, Svelte, Preact, and Beyond: Exploring the legacy-proof (adaptability) benefits of having a completely separate view that matches designs closely
- Part 7 — Decoupler and the future for legacy-proof UI: Further exploration of perks that this approach allows that go even beyond legacy-proof code considerations
In the previous article, we discussed Decoupler MVU pattern for application development that allows us to separate business logic from presentation logic so that we could make our code legacy-proof.
Because our presentation layer is now stateless and unaware of the business logic, it matches the designs very closely.
In Figma, you essentially create the same thing — a stateless component tree. Just like any modern UI framework, Figma relies on components.
Naturally, a question arises: could we use Figma as a single declarative source of truth for our view and eliminate a tedious transfer of designs to code?
And, fortunately, it is not as far-fetched as it might sound!
With Figma having introduced its dev-mode as well as various third-party plugins, it is now possible to convert designs into a stateless component tree in React and other frameworks. This is absolutely mind-blowing, but of course not without its own problems, as many of such code generation plugins miss various features.
Among the problems I faced:
- You can’t pass props in a top-down manner.
- You can’t have arrays to generate lists
But in general, it is possible. In this article, I will show some practical examples that demonstrate the perks of what the complete separation of the view layer from the rest of the app allows to accomplish.
To experiment with this idea, I created a Figma design for a simplified chat.
The main component has a title, message area, and input.
Messages component in turn composes a list of messages which are in turn composes components.
Code generation for storybook
There is a particularly useful plugin for React code generation from Figma — Anima.
It is not perfect, as you still have to fix quite a few things about styles and code here in there, but it does a pretty good job of structuring components.
It also does a good job of generating TypeScript code.
Having this code generated, I had to fix styles and properly pass props, as well as add EventWrapper on top of the interactive components.
I didn’t have to touch any other piece of the logic.
Figma’s ability to generate React code (albeit with some flaws), naturally got me thinking if I could now convert this code to other view frameworks — like Vue, Svelte, and Preact.
These frameworks don’t rely 100% on the same concepts, but the concepts from one framework generally map well to the concepts in other frameworks.
Unfortunately, Anima only generates React code. Other plugins could be used for this task, but I wasn't able to find other code-generation tools that do as good of a job.
Naturally, for this task, I used ChatGPT to help me convert the barebone view layer. And it did pretty well! I still had to fix some issues, but it did 90% bona fide work.
I was feeding it component by component to generate the final result.
How about going cross-platform?
Having proof that I can now change tooling as I please (with a bit of extra work of course but still), why not take it to the next level?
How about we turn our code to Flutter?
Concepts in React and other frameworks don’t correspond 100% to what Flutter relies on, but it is very very close. It maps relatively well, and if we had some extra information that we could generate from Figma, we would be able to map to all frameworks easily.
Initially, I wasn’t using ChatGPT to generate code in this case. I used another plugin in Figma — Figma-to-code.
Unlike Anima, it didn’t do as good of a job with generating components, but it still generated the entire tree. And, most importantly, the generated code worked.
I then broke it down into components and stored them in separate files.
Then I restructured it to match the file structure of the React project.
Once the structure matched my React project, I started converting non-react code to dart with ChatGPT.
ChatGPT did a great job. Some issues with types I was able to fix easily as Dart has great type-checking and hints.
I then regenerated components from React project and put them into the Flutter project.
It was a miracle to see that I was able closely to match all the concepts and structures relatively easily with these great tools.
In this article, I attempted to provide a practical example, of what I mean by making design the single declarative source of truth for UI.
Despite the fact that the code-generation tooling is still quite limited in functionality, it allows for eliminating the duplication of effort of converting a design to a view as well as not being tied to a single framework or library.
The reason I was able to accomplish the conversion of React project to Vue, Svelte, Preact, and Flutter relatively easily was because of the decomposition that we achieved with the MVU pattern.
Maybe ChatGPT would be able to provide me with 90% working code if it was coupling all the logic, but fixing the remaining 10% would definitely be more challenging in that case.
Having a decoupled stateless view layer is easily convertible to view layers written using different technologies.
Converting the pure update function as well as tests is the easiest part as the only issue you might face is working with types, but having tests and various type checks in place you would be able to fix it relatively easily.
Part 7 — Decoupler and the future for legacy-proof UI: Further exploration of perks that this approach allows that go even beyond legacy-proof code considerations
At first, I wanted to have a full-blown copy of ChatGPT UI, but that was too ambitious, you can see my attempt at that in this sandbox
- Anima — Excellent Figma to React code generator
- Figma-to-Code Plugin — Great plugin for generating code for various frameworks from Figma
- Swagger Codegen — An excellent example of a code generation tooling
- Figma Official Website: Figma is a popular tool for UI/UX design and prototyping. With its collaborative interface, it enables multiple designers to work together in real-time. This can be especially handy when working on a legacy-proof UI project, as it allows for a seamless transition from design to code.
- UI Design Handbook by Design+Code: This handbook provides a detailed guide on how to turn your designs into code. It covers everything from working with shapes, icons, and images, to handling typography, layout, and spacing. A perfect resource for developers who want to create pixel-perfect UIs that match the initial designs closely.
- Widget Code Generator by Figma: This article introduces a powerful tool provided by Figma — the Widget Code Generator. It automatically generates code for your designs, helping to streamline the development process and reduce the gap between design and implementation. It can be particularly useful for our legacy-proof UI project, enabling faster and more accurate transition from design to code.