Our First Step Towards Interactive Coding on Figma

softmarshmallow
Grida
Published in
6 min readApr 12, 2022

TL;DR

Demo 👇, Github 👉 https://github.com/gridaco/designto-code

Figma + React + ESBuild = 🪄

As designers and developers ourselves, we’ve always longed for a tool where you can design and directly code at the same time without having to switch between different tabs and tools. Though we’re still far away from fulfilling our wishes, we made a major step forward today with realtime scripting update 👏

We are adding live scripting capability to our design-to-code editor and Figma plugin so you can truly own your code and customize your components however you want them.

  1. Simply import your Figma designs on our website or Figma plugin
  2. Instantly convert the designs into React
  3. Add any custom library, logic, hook, or code to your liking
  4. Enjoy the interactive and beautiful preview of your app/website

Designs are Static.

And we had to use tools like storybook to represent state & logic of the component and manage it visually. But with this esbuild support, we can now just 1) design 2) write logic 3) and done; instead of 1) design → interpret design → write layout → write style → make it responsive → add logic → etc…

ESBuild is a Life Savior 🙏

This was all made possible thanks to ESBuild so thank you Evan Wallace (for those of you who don’t know him, he created ESBuild and is also one of the co-founders of Figma).

ESBuild enables super fast transpiling and has allowed us to go from design input to live react app execution in less than a second 💨

→ And now you have the world’s simplest browser code editor with realtime preview

In bundler.ts

// this is not a working code. for the full code, visit <https://github.com/gridaco/designto-code>
import { build, initialize, Loader } from "esbuild-wasm";
// the esbuild version should match your esbuild-wasm dependency version (use exact version)
async function initEsbuild(){
// initialize must be called only once (otherwise, it will throw error)
await initialize({
wasmURL: "<https://unpkg.com/esbuild-wasm@0.14.34/esbuild.wasm>",
worker: true,
});
}
async function bundle(code: string){
// init esbuild before calling build
const result = await build({
entryPoints: [entry_file_path], // otherwitse just put "index.js" (it doesn't matter since we are using the wasm version)
bundle: true,
write: false,
metafile: true,
legalComments: "none",
plugins: [fetchPlugin(code)], // you'll need your custom plugin here
define: {
global: "window",
// .... you other definition like "process.env.NODE_ENV": `"production"`
},
});
return { ... }
}
/// your custom plugin
import { OnLoadResult, PluginBuild } from "esbuild-wasm";
const fetchPlugin = (
inputCode: string,
) => ({
name: "fetch-plugin",
setup(build: PluginBuild) {
build.onLoad({ filter: entry_file_name }, () => {
return {
loader: "tsx",
contents: inputCode,
};
});
},
});

In app.tsx

// this is not a working code. for the full code, visit <https://github.com/gridaco/designto-code>import React, { useState } from "react";
export default function App() {
const [buildResult, setBuildResult] = useState();
const handleOnCodeChnange = (code: string) => {
bundle(code).then(setBuildResult);
};
return (
<>
<CodeEditor onChange={handleOnCodeChnange} />/
<BuildResultView src={buildResult} />
</>
);
}

Extra — state management & debouncing

// this is not a working code. for the full code, visit <https://github.com/gridaco/designto-code>import React, { useState } from "react";
export default function App() {
const [buildResult, setBuildResult] = useState();
const handleOnCodeChnange = debounce((code) => {
bundle(code).then(setBuildResult);
}, 500);
return (
<>
<CodeEditor onChange={handleOnCodeChnange} />/
<BuildResultView src={buildResult} />
</>
);
}
// you can use `debounce` from lodash-es (or use below snippet)
export function debounce<T extends Function>(
func: T,
wait: number = 50,
immediate?: boolean
) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}

*ESBuild is so fast that you don’t necessarily need to have a debouncer

*Full implementation on Github (link at the end of this post)

Highlights

A bonus feature that came with today’s update:

  • Automatic dependency resolution — no need to install dependencies; just import it and we will handle the rest 😉

Limitations

  • Only one Single File Component (SFC) at a time is supported at the moment but you can import npm libraries (we are adding multi-modules support next week)
  • No relative path import of other designs (also adding next week)
  • Only supports React (ts/js) → more details on this below
  • No type intellisense yet
  • ESBuild-wasm on Safari is unstable (fails on second entry; we think this is safari’s issue related to wasm handling) — https://github.com/gridaco/designto-code/issues/140

Examples

This works with any public npm library that runs in browser environment.

Example video: adding a button disable/enabler for password input

What about other frameworks?

  • Other js based frameworks can be easily supported with current architectures, Vue and Svelte.
  • For react-native, we are taking a look into other techniques called snack (made by expo) snack.expo.devhttps://github.com/expo/snack
  • Flutter, we once had a dart-services support to show Flutter app as a running preview (same backend that dartpad.dev uses). We killed it beacause it was too slow then but, we’ll be bringing it back at the end of the month. So, follow our Twitter if you’re a Flutter lover @grida_co

How about Vite?

TL;DR — we will switch from plain ESBuild to Vite

Vite is a new-born technology it’s great for large scale application but at this moment, we prefer ESBuild as it comes with a nice built-in react support. Eventually though, we will have to use Vite for HRM. We don’t want to be loosing our view state while testing complex state related codes and sadly it looks like ESBuild won’t be having native HMR support for a while.

Here is a very interesting project from the divriots team — browser-vite which allows us to run Vite in browser environment. Check out their blog → https://divriots.com/blog/vite-in-the-browser

Learn more about Vite: https://vitejs.dev/

Next Challenge — DX

Our next challenge is to make this possible for your entire project and allow you to sync your Figma files with your existing repositories.

This is quite challenging as there are 5 different sources and we need a decent rule, ux, and algorithm to be able to handle this in a seamless and intuitive way. The 5 sources are:

  • Figma (the original design file)
  • Code generated from our design2code engine
  • Code altered by transformer logics (e.g. splitting big component into n module files)
  • Code customized by user
  • Repository (final project for production — your Github repo)

There’s bunch of problems to consider — what if a part of the design is removed after the initial import and the user customizes the code? what happens on the next sync? We want the process to be conflict-free but haven’t been able to find an adequate solution yet.

On the Next Update..

In the next few months, we will be focusing on making our editor as a full-scale IDE including:

  • Experimental Vuejs & Svelte support
  • Switching from standalone monaco to vscode
  • Major editor ui/ux renewal to make it more edit-friendly
  • Local & remote full-text search capability
  • Local & remote filesystem

Watch our repos and subscribe to our newsletter for the coming updates 🤙

Contribute on Github — We’re Making Open Source Design-to-Code Solutions

We’re on a journey to making designers’ and developers’ lives easier. And we’ releasing new features every week. Join our open source projects on Github and be a part of building the tools you need.

  • Don’t forget to share this blog and ⭐️🌟💫star & 👀⌚️watch the repo so we can know that you care 😉
  • We are hiring, email or slack me if you want to join our adventure.

Follow us

Please subscribe / follow our medium, github, youtube, facebook, twitter and instagram for latest updates and releases.

#make #grida

P.S. This was an experimental project for us so please feel free to report any issues or errors 🤓 (https://github.com/gridaco/designto-code/issues/new)

--

--