Home >Web Front-end >CSS Tutorial >Building Interactive Figma Widgets

Building Interactive Figma Widgets

William Shakespeare
William ShakespeareOriginal
2025-03-11 09:37:10507browse

Building Interactive Figma Widgets

Figma has always encouraged collaboration between developers and designers and has flourished with its rich community plug-in library. Need 3D elements? There are plugins! Need an abstract SVG? There are plugins too!

However, the design part of Figma has always been relatively static—always used unmovable rectangles, connected together through predefined user interactions. But what would you think if I said your design can suddenly come to life—can be animated, interactive, or even stateful? So, what is the difference between concepts and implementations?

Figma announced in June that it would launch a JavaScript-based widget. Now designers can browse and implement logic-driven components directly in Figma!

Let's learn about the Widgets API together! Want to know what it is and how to use it? This is exactly what we will explore together in this article.

Figma widget enables infinite possibilities

Imagine that you and your partners work day and night to design a large restaurant application. You all collaborate on the same Figma artboard; you share the exact same document and the changes happen in real time.

Of course, you already know that collaboration involves more than just the design process:

  • Project management,
  • Hold a vote,
  • Import and visualize simulated data,
  • Maybe even play a multiplayer game to relax for a long time.

Just one person needs to manage everything and send links to the rest of the group. But, this is not efficient, right?

This is where the widget comes into play. We can imagine doing all of this—yes, all of this—without leaving Figma.

Here are some ways you might want to use widgets in Figma:

  • Create tasks for Jira and Asana
  • Create issues in GitHub
  • Show dynamic data
  • Record voice memo
  • Create a list of tasks
  • Waste timePlay tic toe
  • Track activities
  • Create timers

So and so on. As you can see, there are already a large number of widgets that are free to be used in your documentation. In fact, you can add widgets to your artboard directly from the Widgets menu (Shift I).

But we are not here to learn how to use widgets, because it's easy. Let's do what we do best: We'll create our own Figma widgets! This widget will be inspired by Chris Coyier's design quote website. We'll get the API, feed it to the widget, and then display the random design quotes directly in Figma.

What do we need

I don't like being a spreader of bad news, but in order to develop widgets, you have to be on Windows or Mac. Linux user, sorry, you are not lucky. (If you want to continue learning, you can still use the virtual machine.)

We will download the Figma desktop application. The easiest way to get started is to generate widget templates directly from the application.

Let's create a new artboard by opening the widget menu (Shift I), switching to the Development tab, and creating a new project.

After that, Figma will prompt you to name the new widget and decide whether it is more suitable for designing artboards or FigJam artboards. For the purpose of this article, the previous option is sufficient.

Customization doesn't end there; Figma also gives you the option to start with a prefabricated counter widget or an alternative to enable iFrame, which also allows you to access the Canvas and Fetch APIs (and all other browser APIs). We will choose the simple "empty" option, but we will eventually modify it ourselves to use the Fetch API.

The system will then prompt you to save the new Widget project to a special directory in the system. Once done, start your terminal and direct it to the folder. Don't run any commands now - we'll do it later and we'll deliberately make an error with the goal of learning more about the Widgets API.

Design Widgets

We get the design directly from Chris Coyier's design quote website. So, let's go there and dig deeper by launching DevTools.

The two key shortcuts I'm using here are Ctrl Shift C (or Cmd Shift C) to switch the Pick Elements tool, and Shift clicks to change the color format to HEX code. We do this to understand the colors, fonts, font thickness, and font size used in Chris' website. All this information is crucial to building a very similar widget in Figma, and this will be our next step! You can grab the designed components and use them on your own canvas.

I won't go into details here, because the topic of this article is to build widgets by writing code. But I have to emphasize that it is very important to style your widgets carefully… CSS-Tricks already has a lot of design-oriented Figma tutorials; you won't regret adding them to your reading list.

Create layouts for our Widget

After the design is finished, it's time to take out our programming fingers and start building the gears of our widgets.

How Figma converts its design building blocks into React-like components is very interesting. For example, a framework element with automatic layout is represented in the code as a <autolayout></autolayout> component. In addition, we will use two other components: <text></text> and <svg></svg>.

Look at my Figma artboard...I'm asking you to pay attention to the object tree. This is the key to our need to be able to convert our Widget design into JSX code.

As you can see, our design quote widget requires importing three components. Considering that the full API contains only eight layer-based nodes, this is a considerable number of components. But as you'll see soon, these modules are enough to make all kinds of layouts.

<code>// code.tsx const { widget } = figma; const { AutoLayout, Text, SVG } = widget;</code>

With this, we can build the skeleton of our widgets as in React:

<code>function QuotesWidget() { const quote = `...`; const author = `...`; return ( <autolayout><svg></svg><autolayout><text>{quote}</text><text>— {author}</text></autolayout><svg></svg></autolayout> ); } widget.register(QuotesWidget);</code>

This code is very confusing to say the least. Now, we cannot distinguish between design layers. Fortunately, we can easily solve this problem by using the name attribute.

<code><autolayout name="{" quote><svg name="{" leftquotationmark></svg><autolayout name="{" quotecontent><text name="{" quotetext>{quote}</text></autolayout><svg name="{" rightquotationmark></svg></autolayout>;</code>

Of course, we still can't see our quote SVG, so let's start solving this problem. The <svg></svg> component accepts a src attribute that uses the source code of the SVG element. There is not much to say about this, so let's keep it simple and jump back to the code directly:

<code>const leftQuotationSvgSrc = `<svg fill="none" height="103" viewbox="0 0 117 103" width="117" xmlns="<http://www.w3.org/2000/svg>"> // For simplicity, it has been shortened</svg>`; const rightQuotationSvgSrc = `<svg fill="none" height="103" viewbox="0 0 118 103" width="118" xmlns="<http://www.w3.org/2000/svg>"> // Shortened for simplicity</svg>`; function QuotesWidget() { return ( <svg name="{" leftquotationmark src="%7BleftQuotationSvgSrc%7D"></svg><svg name="{" rightquotationmark src="%7BrightQuotationSvgSrc%7D"></svg> ); }</code>

I think we can all agree that everything is much clearer now! When we name things, their purpose suddenly becomes more obvious to readers of our code.

Preview of our Widget

Figma provides a good development experience when building widgets, including (but not limited to) hot reloading. With this feature, we can encode and preview changes to widgets in real time.

First open the widget menu (Shift I), switch to the Development tab, and then click or drag your new widget to the artboard. Can't find your widget? Don't worry, just click the three-dot menu and import the manifest.json file of your widget. Yes, that's all the steps to get it back to existence!

Wait, is there an error message at the bottom of your screen?

If so, let's investigate. Click "Open Console" and read its contents. If the Open Console button disappears, there is an alternative way to open the debug console. Click the Figma logo, jump to the Widgets category and display the development menu.

The error may be because we have not compiled TypeScript to JavaScript yet. We can do this on the command line by running npm install and npm run watch (or yarn and yarn watch). There are no errors this time!

Another hurdle you may encounter is that the widget cannot be re-rendered every time the code changes. We can easily force widget updates using the following context menu command: WidgetRe-render widget.

Style Settings Widget

For now, the appearance of our widgets is still far from our ultimate goal.

So how do we style the Figma component from the code? Maybe using CSS like you would in a React project? mistake. For Figma widgets, all styles are implemented through a complete set of properties. Fortunately, these projects have almost the same name as the corresponding projects in Figma.

We will first configure our two <autolayout></autolayout> components. As shown in the figure above, the attribute names describe their purpose very clearly. This allows us to jump straight to the code and start making some changes. I won't show the entire code again, so rely on the component name to guide you where the code snippet is.

<code><autolayout direction="{" horizontal horizontal:="" center name="{" quote padding="{{" spacing="{54}" vertical:="" vertical start quotecontent verticalalignitems="{" end></autolayout>;</code>

We have made great progress! Let's save and jump back to Figma to see how our Widget looks. Remember how Figma automatically reloads the Widget after a new change?

But it is not enough. We also have to add background color to the root component:

<code><autolayout fill="{" name="{" quote></autolayout></code>

Similarly, look at your Figma artboard and notice how the changes are reflected almost immediately into the widget.

Let's continue with this guide and style the <text></text> component.

After viewing the Widgets API documentation, you clearly see again that the property name is almost the same as its corresponding item in the Figma application, as shown in the figure above. We will also use the values ​​of the Chris website in the previous section.

<code><text fill="{'#545454'}" fontfamily="{'Lora'}" fontsize="{36}" fontweight="{'normal'}" name="{'QuoteText'}" width="{700}">{quote}</text><text fill="{'#16B6DF'}" fontfamily="{'Raleway'}" fontsize="{26}" fontweight="{'bold'}" name="{'QuoteAuthor'}" textcase="{'upper'}" width="{700}">— {author}</text></code>

Add status to the Widget

Our widget currently displays the same quote, but we want to extract it randomly from the entire quote pool. We have to add a state to our widget, which all React developers know is a variable whose changes will trigger a re-render of our component.

In Figma, the state is created using the useSyncedState hook; it is almost React's useState, but it requires the programmer to specify a unique key. This requirement stems from the fact that Figma must synchronize the states of our Widgets that span all clients that may be viewing the same design artboard, but through different computers.

<code>const { useSyncedState } = widget; function QuotesWidget() { const [quote, setQuote] = useSyncedState("quote-text", ""); const [author, setAuthor] = useSyncedState("quote-author", ""); }</code>

Currently, this is all the changes we need to make. In the next section, we will figure out how to get data from the internet. Spoiler Warning: This is not as simple as it seems.

Get data from the network

Recall Figma lets us choose to start with a widget that enables iFrame. While we didn't choose that option, we still had to implement some of its features. Let me explain why we can't simply call fetch() in widget code.

When you use widgets, you are running JavaScript code written by someone else on your computer. While all widgets have been thoroughly scrutinized by Figma staff, this is still a huge security vulnerability because we all know how much damage even a line of JavaScript can cause.

So Figma cannot simply eval()anonymously any widget code written by a programmer. Long story short, the team decided that the best solution was to run third-party code in a tightly protected sandbox environment. As you might have guessed, the browser API is not available in this environment.

But don't worry, Figma's solution to this second problem is <iframe></iframe>. Any HTML code we write in a file (preferably called ui.html) has access to all browser APIs. You may be wondering how we can trigger this code from a Widget, but we'll look into this later. Now, let's go back to the code:

<code>// manifest.json { "ui": "ui.html" }</code>
<code> window.onmessage = async (event) => { if (event.data.pluginMessage.type === 'networkRequest') { // TODO: Get data from the server window.parent.postMessage({ pluginMessage: { // TODO: Return the retrieved data} }, '*') } } </code>

This is a common template for Widget to iFrame communication. Let's use it to get data from the server:

<code> window.onmessage = async (event) => { if (event.data.pluginMessage.type === 'networkRequest') { // Get random number from 0 to 100 const randomPage = Math.round(Math.random() * 100) // Get random quote from Design Quotes API const res = await fetch(`https://quotesondesign.com/wp-json/wp/v2/posts/?orderby=rand&per_page=1&page=${randomPage}&_fields=title,yoast_head_json`) const data = await res.json() // Extract authorName and quote content from the response const authorName = data[0].title.rendered const quoteContent = data[0].yoast_head_json.og_description window.parent.postMessage({ pluginMessage: { authorName, quoteContent } }, '*') } } </code>

To keep it simple and clear, we omit error handling. Let's go back to the widget code and see how we access the functions defined in <iframe></iframe>:

<code>function fetchData() { return new Promise<void>(resolve => { figma.showUI(__html__, {visible: false}) figma.ui.postMessage({type: 'networkRequest'}) figma.ui.onmessage = async ({authorName, quoteContent}) => { setAuthor(authorName) setQuote(quoteContent) resolve() } }) }</void></code>

As you can see, we first tell Figma to publicly access our hidden <iframe></iframe> and trigger an event called "networkRequest". We handle this event in the ui.html file by checking event.data.pluginMessage.type === 'networkRequest' and then publish the data back to the widget.

But nothing has happened yet...We still haven't called the fetchData() function. If we call it directly in the component function, the following error will appear in the console:

<code>ShowUI cannot be used during widget rendering. </code>

Figma tells us not to call showUI directly in the function body... So where should we put it? The answer is a new hook and a new function: useEffect and waitForTask. If you are a React developer, you may already be familiar with useEffect, but we will use it here to get data from the server when the widget component is mounted.

<code>const { useEffect, waitForTask } = widget; function QuotesWidget() { useEffect(() => { waitForTask(fetchData()); }); }</code>

But this will cause another "error", and our widget will always be re-rendered with the new quote. This happens because useEffect by definition, it will fire again whenever the state of the Widget changes, or when we call fetchData. While there is a technique that can only call useEffect once in React, it does not work with the implementation of Figma. From Figma documentation:

Due to how widgets run, useEffect should handle being called multiple times using the same state.

Luckily, we can take advantage of a simple workaround that only calls useEffect once the component is first mounted by checking whether the state's value is still empty:

<code>function QuotesWidget() { useEffect(() => { if (!author.length & !quote.length) { waitForTask(fetchData()); } }); }</code>

You may encounter a terrible "Memory Access Out of Boundary" error. This is very common in plug-in and widget development. Just restart Figma and it won't appear again.

You may have noticed that sometimes quote text contains strange characters.

These are Unicode characters, we have to format them correctly in the code:

<code> window.onmessage = async (event) => { // ... const quoteContent = decodeEntities(data[0].yoast_head_json.og_description); }; // <https:> var decodeEntities = (function () { // this prevents any overhead from creating the object each time var element = document.createElement("div"); function decodeHTMLEntities(str) { if (str && typeof str === "string") { // strip script/html tags str = str.replace(/]*>([\\\\S\\\\\s]*?)<\\\\\/script>/gim, ""); str = str.replace(/<\\\\\/?\\\\\\w(?:[^"&#39;>]|"[^"]*"|'[^']*')*>/gim, ""); element.innerHTML = str; str = element.textContent; element.textContent = ""; } return str; } return decodeHTMLEntities; })(); </https:></code>

Voice, our Widget gets a brand new design quote every time it is added to the design artboard.

Add properties menu to our widget

While our widget gets a new quote when instantiated, it will be more practical if we can perform this process again without removing it. This section will be briefly introduced because the solution is excellent. Using the properties menu, we can add interactivity to our Widget by calling the usePropertyMenu hook at once.

<code>const { usePropertyMenu } = widget; function QuotesWidget() { usePropertyMenu( [ { itemType: "action", propertyName: "generate", tooltip: "Generate", icon: `<svg fill="none" height="15" viewbox="0 0 22 15" width="22" xmlns="<http://www.w3.org/2000/svg>"></svg>`, }, ], () => fetchData() ); }</code>

Using a simple hook, we can create a button that appears near our Widget when our Widget is selected. This is the last part we need to add to complete this project.

Publish our Widget to the public

If no one uses it, then building a Widget is of little use. While Figma allows organizations to launch privateWidgets for internal use, it is more common to publish these applets around the world.

Figma has a meticulous widget review process that can take between 5 and 10 working days. While the design quote widget we built together is already in the widget library, I will still demonstrate how it gets there. Please do not try to publish this widget again, as this will only result in deletion. However, if you make some major changes to it, keep sharing your own widgets with the community!

First click on the Widget menu (Shift I), and then switch to the Development tab to view our Widget. Click the three-dot menu and press Publish.

Figma will prompt you to enter some details about your Widget, such as title, description, and some tags. We also need a 128×128 icon image and a 1920×960 banner image.

After importing all these resources, we still need a screenshot of the Widget. Turn off publish mode (don't worry, you won't lose data) and right-click on the Widget to show an interesting context menu. Find the Copy/Paste as category and select Copy as PNG.

After this is done, let's go back to publish mode and paste the screenshot of the Widget in:

Scroll down and finally publish your mode. celebrate! ?

Figma will contact you in a few days to inform you of the status of the model review. If rejected, you will have the opportunity to make the changes and submit them again.

Conclusion

We just built a Figma widget from scratch! There is a lot that is not covered here, such as click events, input forms, and more. You can dig into the full source code of Widget in this GitHub repository.

For those who are eager to take their Figma skills to the next level, I recommend exploring the Widgets community and using what interests you as inspiration. Keep building more widgets, keep honing your React skills, and you'll teach me how to do it all before you realize it.

More resources

I had to refer to a lot of documentation when making this widget. I think I'll share what I found most helpful.

Build more widgets:

  • Best practices for building widgets
  • Official Figma widget examples with code

Learn more deeply widgets:

  • All widgets hooks
  • All widgets
  • How widgets run behind the scenes

Widgets and plugins

  • Widgets and plugins
  • Introduction to Figma plugins
  • How plugins run behind the scenes

The above is the detailed content of Building Interactive Figma Widgets. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn