Building Interactive Figma Widgets

Figma has always encouraged collaboration between developers and designers. It strives on an endless treasury of community-made plugins. Need 3D elements? There’s a plugin for that. Need abstract SVGs? There’s a plugin for that, too.

That said, the design part of Figma has always been relatively static — always working with unmovable rectangles connected to each other through predefined user interactions. But what if I told you that your designs could suddenly come to life — that they could be animated, interactive, and even stateful? Then, what would separate concept from implementation?

Figma announced in June that it’s bringing JavaScript-powered widgets to the table. Now, designers have can browse and implement logic-driven components straight in Figma!

Say hello to the Widgets API! You want to know what it is and how to use it? That’s exactly what we’re going to do together in this post.

Figma widgets open up tons of possibilities

Imagine that you’re working around the clock with your partner to design a large restaurant application. You’re both already collaborating on the same Figma board; both of you are sharing the exact same document with changes happening on the fly.

Surely, you already know that collaboration involves more that just the design process:

  • project management,
  • hosting polls to gather votes,
  • importing and visualizing mock data,
  • and perhaps even playing a multiplayer game to cool-off after many hours of work.

We just require one person to manage everything and send-out links to other members of the group. But oh, that’s not very efficient, is it?

Well, that’s where widgets come into play. We can conceivably do all of that — yes, everything —without ever leaving Figma.

Here are just a few of the ways you might want to use widgets in Figma:

The list goes on and on. As you can tell, there’s already a plethora of widgets that you can freely use in your documents. In fact, you can add Widgets straight to your board from the Widgets menu (Shift+I).

But we’re not here to learn how to use widgets, because that’s easy. Let us do what we do best: we’re gonna create our own Figma widget! This one will be inspired by Chris Coyier’s design quotes website. We’ll take the API, feed it into the widget, then display random design quotes directly in Figma.

Here’s what we need

I don’t like to be the bearer of bad news, but in order to develop widgets, you must be on Windows or Mac. Linux users, I’m sorry, but you’re out of luck. (You could still use a VM if you want to follow along.)

We’re gonna download the Figma Desktop application. The simplest way to get started is by generating a widget template, straight from the app.

Let’s create a new board by opening the widgets menu (ShiftI), switching to the Development tab, and creating a new item.

Following that, Figma will prompt you to name the new widget and decide whether it’s more tailored towards design boards or FigJam boards too. The former option is sufficient for the purposes of this article.

And the customization doesn’t end here; Figma will also give you the option to start with a pre-made counter widget or an iFrame-enabled alternative that also gives you access to the Canvas and Fetch APIs (as well as all other browser APIs). We’ll go with the simple “Empty” option, but we’ll eventually modify it ourselves to make use of the Fetch API.

You’ll then be prompted to save your new widget project to a special directory in your system. Once that’s done, launch your terminal and direct it to that folder. Don’t run any commands yet — we’ll do that later and purposefully get an error with the goal of learning more about the Widgets API.

Designing the widget

We’re pulling the design straight from Chris Coyier’s design quotes website. So, let’s go there and dive into by firing up DevTools.

The two key shortcuts that I’m using here are Ctrl+Shift+C (or Cmd+Shift+C) to toggle the “Pick element” tool, and Shift+Click to change the color format to HEX code. We’re doing this to learn about the colors, fonts, font weights and font sizes used in Chris’s website. All this information is critical to build a closely-resembling widget in Figma, which will be our next step! You can grab the designed component and use it in your own canvas.

I won’t go into much detail here as this article’s main topic is building widgets by writing code. But I can’t stress enough how important it is to take good care of your widgets’ style… CSS-Tricks already has a plethora of design-oriented Figma tutorials; you won’t regret adding them to your reading list.

Creating the layout for our widget

With design out of the way, it’s time to take our programming fingers out and start building the gears of our widget.

It’s very interesting how Figma translates its design building blocks to React-like components. Frame elements with the auto-layout feature, for example, are represented as the <AutoLayout /> component in code. In addition to that, we’ll be using two more components: <Text /> and <SVG />.

Take a look at my Figma board… I’m precisely asking you to focus on the object tree. It’s what we need to be able to translate our widget design to JSX code.

As you can see, our design quotes widget demands three components to be imported. That’s a decent number of components considering that the full API only contains eight layer-based nodes. But as you’ll soon see, these modules are more than sufficient to craft all kinds of layouts.

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

And with this, we have all we need to go ahead and build the skeleton of our widget like we would in React:

function QuotesWidget() {
  const quote = `...`;
  const author = `...`;

  return (
    <AutoLayout>
      <SVG />
      <AutoLayout>
        <Text>{quote}</Text>
        <Text>— {author}</Text>
      </AutoLayout>
      <SVG />
    </AutoLayout>
  );
}

widget.register(QuotesWidget);

This code is very confusing, to say the least. Right now, we can’t tell the design layers apart. Thankfully, we’re able to easily solve this issue through the use of the name property.

<AutoLayout name={"Quote"}>
  <SVG name={"LeftQuotationMark"} />
  <AutoLayout name={"QuoteContent"}>
    <Text name={"QuoteText"}>{quote}</Text>
    <Text name={"QuoteAuthor"}>— {author}</Text>
  </AutoLayout>
  <SVG name={"RightQuotationMark"} />
</AutoLayout>;

And, of course, we still can’t see our quotation mark SVGs, so let’s work on fixing that. The <SVG/> component accept a srcproperty that takes the source code for an SVG element. There isn’t much to say on this one, so let’s keep it simple and jump straight back to code:

const leftQuotationSvgSrc = `<svg width="117" height="103" viewBox="0 0 117 103" fill="none" xmlns="<http://www.w3.org/2000/svg>">
  // shortened for brevity
</svg>`;
const rightQuotationSvgSrc = `<svg width="118" height="103" viewBox="0 0 118 103" fill="none" xmlns="<http://www.w3.org/2000/svg>">
// shortened for brevity
</svg>`;

function QuotesWidget() {
  return (
    <SVG name={"LeftQuotationMark"} src={leftQuotationSvgSrc} />
    <SVG name={"RightQuotationMark"} src={rightQuotationSvgSrc} />
  );
}

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

Previewing our widget in real-time

Figma offers a great developer experience when building widgets, including (but not limited to ) hot-reloading. With this feature, we’re able to code and preview changes to our widget in real-time.

Get started by opening the widgets menu (Shift+I), switching to the development tab and clicking or dragging your new widget to the board. Unable to locate your widget? Don’t worry, just click on the three-dot menu and import your widget’s manifest.json file. Yes, that’s all it takes bring it back to existence!

Wait, did you get an error message at the bottom of your screen?

If so, let’s investigate. Click on “Open console” and read what it has to say. If the Open console button is gone, there’s an alternative way to open the debugging console. Click on the Figma logo, jump to the widgets category and reveal the development menu.

That error is likely due to the fact that we haven’t compiled our TypeScript to JavaScript yet. We can do that in the command line by running npm install and npm run watch. (or yarn and yarn watch ). No errors this time!

One more obstacle you might hit is that the widget fails to re-render any time the code is changed. We can easily force our widget to update using the following context menu command: Widgets → Re-render widget.

Styling the widget

As it currently stands, the looks of our widgets are still pretty far from our final goal.

So how do we style Figma components from code? Maybe with CSS like we would do in a React project? Negative. With Figma widgets, all the styling happens through a set of well-documented props. Lucky for us, these items are named almost identically to their counterparts in Figma.

We’ll get started by configuring our two <AutoLayout /> components. As you can see in the infographic above, prop names are pretty descriptive of their purpose. This makes it easy for us to jump straight into code and start making some changes. I won’t be showing the whole code again, so please rely on the component names to guide you where the snippets belongs.

<AutoLayout
  name={"Quote"}
  direction={"horizontal"}
  verticalAlignItems={"start"}
  horizontalAlignItems={"center"}
  spacing={54}
  padding={{
    horizontal: 61,
    vertical: 47,
  }}
>
  <AutoLayout
    name={"QuoteContent"}
    direction={"vertical"}
    verticalAlignItems={"end"}
    horizontalAlignItems={"start"}
    spacing={10}
    padding={{
      horizontal: 0,
      vertical: 0,
    }}
  ></AutoLayout>
</AutoLayout>;

We just made a lot of progress! Let’s save and jump back to Figma to see how our widget looks like. Remember how Figma reloads widgets automatically upon new changes?

But it’s not quite there yet. We must also add a background color to the root component:

<AutoLayout name={"Quote"} fill={"#ffffff"}>

Again, take a look at your Figma board and notice how changes can be reflected almost immediately back into the widget.

Let’s move along this guide and style the <Text> components.

After taking a look at the Widgets API documentation, it’s again clear that property names are almost identical to their counterparts in the Figma app, as can be seen in the infographic above. We’ll also be using values from the last section where we inspected Chris’ website.

<Text name={'QuoteText'}
  fontFamily={'Lora'}
  fontSize={36}
  width={700}
  fill={'#545454'}
  fontWeight={'normal'}
>{quote}</Text>

<Text name={'QuoteAuthor'}
  fontFamily={'Raleway'}
  fontSize={26}
  width={700}
  fill={'#16B6DF'}
  fontWeight={'bold'}
  textCase={'upper'}
>— {author}</Text>

Adding state to the widget

Oour widget currently displays the same quote, but we want to pull from the entire pool of quotes at random. We must add state to our widget, which all React developers know is a variable whose change triggers the re-rendering of our component.

With Figma, state is created with the useSyncedState hook; it’s pretty much React’s useState, but it requires programmers to specify a unique key. This requirement stems from the fact that Figma must sync our widget’s state across all clients that may be viewing the same design board, but through different computers.

const { useSyncedState } = widget;

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

That’s all the change that we need for now. In the next section, we’ll figure out how to fetch data from the Internet. Spoiler Alert: it’s not as simple as it seems.

Fetching data from the network

Recall when Figma gave us the choice to start with an iFrame-enabled widget. Although we didn’t go with that option, we must still implement some of its features. Let me explain why we can’t simply call fetch() within our widget code.

When you use a widget, you are running JavaScript code on your own computer that’s written by someone else. While all widgets are thoroughly reviewed by the Figma staff, it’s still a huge security hole as we all know how much damage can be created by even one line of JavaScript.

As a result, Figma cannot simply eval() any widget code written by anonymous programmers. Long story short, the team decided that the best solution was running third-party code in a closely-guarded sandbox environment. And as you might have guessed, browser APIs are unavailable in such an environment.

But don’t fret, Figma’s solution to this second problem is <iframe>s. Any HTML code that we write in a file, preferably called ui.html, will have access to all browser APIs. You might be wondering how we can trigger this code from the widget, but we’ll look into that later. Right now, let’s jump back into code:

// manifest.json
{
  "ui": "ui.html"
}
<!-- ui.html -->
<script>
window.onmessage = async (event) => {
  if (event.data.pluginMessage.type === 'networkRequest') {
    // TODO: fetch data from the server

    window.parent.postMessage({
      pluginMessage: {
        // TODO: return fetched data
      }
    }, '*')
  }
}
</script>

That’s the general template for widget-to-iframe communication. Let’s use it to fetch data from the server:

<!-- ui.html -->
<script>
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 a random quote from the 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 author name and quote content from response
    const authorName = data[0].title.rendered
    const quoteContent = data[0].yoast_head_json.og_description

    window.parent.postMessage({
      pluginMessage: {
        authorName,
        quoteContent
      }
    }, '*')
  }
}
</script>

We’re leaving out error-handling to keep this simple and to-the-point. Let’s jump back into the widget code and see how we access functions defined in the <iframe>:

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()
    }
  })
}

As you can see, we’re first telling Figma to expose access to our hidden <iframe> and to trigger an event with the name "networkRequest". We’re handling this event in the ui.html file by checking event.data.pluginMessage.type === 'networkRequest', and then posting data back to the widget.

But nothing is happening yet… We still haven’t called the fetchData() function. If we call it directly in the component function, the following error occurs in the console:

Cannot use showUI during widget rendering.

Figma is telling us not to call showUI directly in the function body… So, where should we put it? The answer to that is one new hook and one new function: useEffect and waitForTask. You might already have familiarity with useEffect if you’re a React developer, but we’re gonna use it here to fetch data from the server when the widget component mounts.

const { useEffect, waitForTask } = widget;

function QuotesWidget() {
  useEffect(() => {
    waitForTask(fetchData());
  });
}

But this will result in yet another “error” where our widget will keep re-rendering with a new quote, forever. This happens because useEffect, by definition, triggers again whenever the widget’s state changes, nay when we call fetchData. And while there’s a technique to only call useEffect once in React, it does not work on Figma’s implementation. From Figma’s docs:

Because of How Widgets Run, useEffect should handle being called multiple times with the same state.

Thankfully, there’s a simple workaround that we can take advantage of and call useEffect only once when the component first mounts, and it’s by checking whether or not the state’s values are still empty:

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

You might run into a scary “memory access out of bounds” error. It’s quite common to see in plugin and widget development. Just restart Figma and it won’t be there anymore.

You might have noticed that sometimes, the quote text contains weird characters.

These are Unicode characters and we must properly format them in code:

<!-- ui.html -->
<script>
window.onmessage = async (event) => {
  // ...
  const quoteContent = decodeEntities(data[0].yoast_head_json.og_description);
};

// <https://stackoverflow.com/a/9609450>
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(/<script[^>]*>([\\\\S\\\\s]*?)<\\\\/script>/gim, "");
      str = str.replace(/<\\\\/?\\\\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gim, "");
      element.innerHTML = str;
      str = element.textContent;
      element.textContent = "";
    }

    return str;
  }

  return decodeHTMLEntities;
})();
</script>

And voilà, our widget fetched a brand new design quote every single time it’s added to the design board.

Adding a property menu to our widget

While our widget fetches a fresh quote upon instantiation, it would be much more practical if we could do this process again but without deleting it. This section will be short as the solution is quite remarkable. With property menus, we can add interactivity to our widget with a single call to the usePropertyMenu hook.

Credit: Figma Docs.
const { usePropertyMenu } = widget;

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

With one simple hook we’re able to create a button that appears near our widget when it’s selected. That was the last piece that we needed to add in order to complete this project.

Publishing our widget to the public

There’s not much use in building a widget if, well, no one uses it. And while Figma grants organizations with the option to launch private widgets for internal use, it’s much more common to release these little programs to the world.

Figma has a delicate widget review process that may take up 5 to 10 business days. And while the design quotes widget we built together is already in the widget library, I will still demonstrate how it got there. Please don’t attempt to re-publish this widget again as that will only result in removal. But if you gave it some significant alterations, go ahead and share your own widget with the community!

Get started by clicking the widgets menu (Shift+I) and switching to the Development tab to view our widget. Click on the three-dots menu and press Publish.

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

After importing all these assets, we still need a screenshot of our widget. Close the publishing modal (don’t worry, you won’t lose your data) and right-click on the widget to reveal an interesting context menu. Find the Copy/Paste ascategory and select Copy as PNG.

With that done, let’s go back to the publishing modal and paste the widget’s screenshot:

Scroll down and finally publish your modal. Celebrate! 🎉

Figma will reach out to you in a couple of days about the status of your modal’s review. In the case of a rejection, you’ll be given the opportunity to make changes and submit again.

Conclusion

We just built a Figma widget from scratch! There are many things not covered here, such as click eventsinput forms, and much more. You can dig into the full source code for the widget in this GitHub repo.

To those who aspire to take their Figma skills to greater levels, I suggest exploring the Widgets community and using what catches your eye as inspiration. Keep building more widgets, keep sharpening your React skills, and before you even realize it, you’ll be teaching me how to do all this.

Further resources

I had to refer to lots of documentation while I was making this widget. I thought I’d share what I found to help the most.

Build more widgets:

Learn widgets in greater depth:

Widgets vs. plugins


Building Interactive Figma Widgets originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Technical Writing for Developers

HTML, CSS, JavaScript, Python, PHP, C++, Dart — there are so many programming languages out there and you may even be totally fluent in several of them! But as we aim to write more and better code, the way we write and communicate in everyday language becomes more and more important… and perhaps even overlooked.

The way we write about and around code is arguably as important as the code itself. And despite where you fall on that line, we can all agree that our words have the potential to both help and hurt code’s effectiveness.

In this article, I want to outline how these two seemingly distinct fields — programming and writing — can come together and take our developer skills to the next level.

Wait, technical writing? Yes, that’s exactly what I mean. I truly believe we are all writers in one sense or another. And I’m here to give you a primer with writing tips, advice, and examples for how it can make you both a better developer and communicator.

Technical writing is everywhere

Last year, the team behind the popular Mac Git client, Tower, polled more than 4,000 developers and found that nearly 50% of them spent between 3-6 hours a day writing code.

Bar chart showing actual programming time per day.

And yes, that’s one survey polling a pretty niche group, but I imagine many of us fall somewhere in that range. Whatever the case, a developer isn’t writing code 24/7, because as this poll suggests, we’re spending plenty of time doing other things.

That might include:

  • demoing a new feature,
  • documenting that new feature,
  • updating a work ticket related to that new feature, or
  • backlogging work to support that new feature.

Of course, there’s always time for bathroom breaks and Wordle too.

Anyway, most of the things we typically do involve communicating with people like your team, colleagues, clients, users, and other developers.

So we do spend a good chunk of our time communicating with humans through words in addition to the communication we have with computers through code. Words are written language. And if we wrote our words better, we’d communicate better. When we communicate better, we’re more likely to get what we want.

That’s Technical Writing 101.

Venn diagram showing the overlap between technical writing and coding.

And it doesn’t even end here.. Some programmers also like to make their own products, which means they need to make marketing part of their job. Technical writing plays a huge role in that too. So, yeah. I think it’s pretty fair to say that technical writing is indeed everywhere.

What is good grammar?

With so many programming languages out there, the last thing we want is to learn another one.

Grammar is an integral part of English, and it unlocks the full potential of communication. It makes us more formal, professional, and coherent.

Let me give you a quick rundown on language.

The English syntax

Just like programming languages, English has a well-defined syntax, and it starts with words.

Words are the building blocks of English, and they fall into eight buckets:

Color coded sentence showing the English syntax.
Nouns

These can be names of people, animals, places, concepts, and objects.

Example:
CSS is one of the core languages of front-end development.

Verbs

Verbs convey action. Even “is” can be considered an action.

Example:
Marcia codes in the morning and answers emails in the afternoon.

Adjectives

Adjectives are how we describe nouns. They’re like meta that adds more detail to a sentence to paint a vivid picture.

Examples:

  • CSS is an elegant and poetic language.
  • The HTML for tables is complex and cumbersome.
  • The Box Model is important to understand CSS.
Prepositions

Prepositions create a relationship between a noun and other words, often indicating direction, time, location, and space.

Examples:

  • Did you commit your work to the repo?
  • What is the best approach for this component?
  • We conducted interviews with real users.
Adverbs

Sometimes actions need to be more specific, so we use adverbs such as “runs fast” and “compiles slowly.” They often end in “-ly.”

Examples:

  • This is easily the best idea of them all.
  • Chip waited patiently for Dale’s feedback.
  • The team worked diligently on the project.
Conjunctions

Conjunctions connect phrases in a sentence. Remember this classic song from the show School House Rocks?

Examples:

  • CSS for styling while HTML is for markup.
  • Yes, I write code, but I also work on design.
  • That fixes the bug. Yet it introduced a new one.
Transitions

Paragraphs are made of sentences that are connected to each other using transitions.

Examples:

  • There are many programming languages. However, only a few are used in the web industry.
  • First, clone the directory.
  • I like this approach but on the other hand, I know another one.
Pronouns

When nouns become repetitive, we replace them with pronouns such as: “he,” “it,” and “that.”

Examples:

  • CSS is a stylesheet language. We use it to style websites.
  • Tony loves to code and he practices every day.
  • Our customers are tech-savvy because they know code.

Think of these like UI components: they are modular pieces you can move around to construct a complete and robust sentence, the same way you might piece together a complete and robust UI. Do all of the components need to be there all of the time? Certainly not! Assemble a sentence with the pieces you need to complete the experience, just as you would with an interface.

Voice and tone

Vocabulary, punctuation, sentence structure, and word choice. These are all the ingredients of English. We use them to share ideas, communicate with our friends and family, and send emails to our coworkers.

But it’s crucial to consider the sound of our messages. It’s amazing how one exclamation point can completely shift the tone of a message:

  1. I like programming.
  2. I like programming! :)

It’s easy to confuse voice for tone, and vice versa.

Voice is what concerns our choice of words, which depends on context. For example, a tutorial for beginners is more likely to use slang and informal language to convey a friendly voice, whereas documentation might be written in a formal, serious, and professional manner in an effort to get straight to the point.

The same message, written in two different voices:

  • Fun: “Expand your social network and stay updated on what’s trending now.”
  • Serious: “Find jobs on one of the largest social networking apps and online jobs market.”

It’s not unusual to accidentally write messages that come across as condescending, offensive, and unprofessional. This is where tone comes into play. Read your messages out loud, get other people to read them for you, and experiment with your punctuation and sentence structure. That’s how you hone your tone.

Here’s another way to think of it: your voice never changes, but your tone does. Your voice is akin to who you are as a person, whereas tone is how you respond in a given situation.

Active and passive voice

A sentence always contains an actor, a verb, and a target. The order in which these come determines if the sentence is written in an active or passive voice.

The actor comes first in an active voice. For example: “CSS paints the background.”

Sentences that use an active voice are more straightforward than their counterparts. They’re clearer, shorter, and more understandable — perfect for a more professional voice that gets straight to the point.

With a passive voice, the actor comes last. (See what I did there?) That means our actor — CSS in this case — comes at the end like this: “The background is painted by CSS.”

Readers usually convert a passive voice to an active voice in their heads, resulting in more processing time. If you’ve ever heard that writing in an active voice is better, this is usually the reason why. Tech writers prefer the active voice most of the time, with very few exceptions such as citing research: “It has been suggested that …”

But that doesn’t mean you should always strive for an active voice. Switching from one to the other — even in the same paragraph — can make your content flow more seamlessly from one sentence to another if used effectively.

Avoiding mistakes

Grammar is all about the structure and correctness of language, and there’s nothing better to achieve that than a quick proofreading of your document. It’s very important to rid your writings of spelling mistakes, grammar issues, and semantic imperfections.

At the end of this article, I’ll show you the invaluable tools that professionals use to avoid writing mistakes. Obviously, there are built-in spell checkers in just about everything these days; our code editors even have spell-checking and linting plugins to help prevent mistakes. 

But if you’re looking for a one-stop tool for all-things grammar, Grammarly is one of the most widely-used tools. I’m not getting a kickback for that or anything. It’s just a really great tool that many editors and writers use to write clean and clear content — similar to how you might use Emmet, eslint, or any other linter to write clean and clear code.

Writing code comments

The things we write for other developers can have a big impact on the overall quality of our work, whether it’s what we write in the code, how we explain the code, or how we give feedback on a piece of code.

It’s interesting that every programming language comes with a standard set of features to write a comment. They should explain what the code is doing. By that, I don’t mean vague comments like this:

red *= 1.2 // Multiply `red` by 1.2 and re-assign it

Instead, use comments that provide more information:

red *= 1.2 // Apply a 'reddish' effect to the image

It’s all about context. “What kind of program am I building?” is exactly the kind of question you should be asking yourself.

Comments should add value

Before we look at what makes a “good” code comment, here are two examples of lazy comments:

const age = 32 // Initialize `age` to 32
filter: blur(32px); /* Create a blur effect with a 32px radius */

Remember that the purpose of a comment is to add value to a piece of code, not to repeat it. If you can’t do that, you’re better off just leaving the code as-is. What makes these examples “lazy” is that they merely restate what the code is obviously doing. In this case, the comments are redundant because they tell us what we already know — they aren’t adding value!

Comments should reflect the current code

Out-of-date comments are no rare sight in large projects; dare I say in most projects.

Let’s imagine David, a programmer and an all-around cool guy to hang out with. David wants to sort a list of strings alphabetically from A to Z, so he does the obvious in JavaScript:

cities = sortWords(cities) // sort cities from A to Z

David then realizes that sortWords() actually sorts lists from Z to A. That’s not a problem, as he can simply reverse the output:

cities = sortWords(cities) // sort cities from A to Z
cities = reverse(cities)

Unfortunately, David didn’t update his code comment.

Now imagine that I didn’t tell you this story, and all you saw was the code above. You’d naturally think that after running that second line of code, `cities` would be sorted from Z to A! This whole confusion fiasco was caused by a stale comment.

While this might be an exaggerated example, something similar can (and often does) happen if you’re racing against a close deadline. Thankfully, this can be prevented by following one simple rule… change your comments the same time you change the code.

That’s one simple rule that will save you and your team from a lot of technical debt.

Now that we know what poorly written comments look like, let’s look at some good examples.

Comments should explain unidiomatic code

Sometimes, the natural way of doing things isn’t right. Programmers might have to “break” the standards a bit, but when they do, it’s advisable to leave a little comment explaining their rationale:

 function addSetEntry(set, value) {    
  /* Don't return `set.add` because it's not chainable in IE 11. */  
  set.add(value);
  return set;
}

That’s helpful, right? If you were responsible for reviewing this code, you may have been tempted to correct it without that comment there explaining what’s up.

Comments can identify future tasks

Another useful thing to do with comments is to admit that there’s more work to be done.

// TODO: use a more efficient algorithm
linearSort(ids)

This way, you can stay focused on your flow. And at a later date, you (or someone else) can come back and fix it.

So, you just found a solution to your problem on StackOverflow. After copy-pasting that code, it’s sometimes a good thing to keep a link to the answer that helped you out so you can come back to it for future reference.

Screenshot of copying a link at StackOverflow.
// Adds handling for legacy browsers
// https://stackoverflow.com/a/XXXXXXX

This is important because solutions can change. It’s always good to know where your code came from in case it ever breaks.

Writing pull requests

Pull requests (PRs) are a fundamental aspect of any project. They sit at the heart of code reviews. And code reviews can quickly become a bottleneck in your team’s performance without good wording.

A good PR description summarizes what change is being made and why it’s being made. Large projects have a pull request template, like this one adapted from a real example:

## Proposed changes
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.

## Types of changes
What types of changes does your code introduce to Appium?
 - [ ] Bugfix (non-breaking change which fixes an issue)
 - [ ] New feature (non-breaking change which adds functionality)
 - ...

## Checklist
 - [ ] I have read the CONTRIBUTING doc
 - [ ] I have signed the CLA
 - [ ] Lint and unit tests pass locally with my changes

## Further comments
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc…

Avoid vague PR titles

Please avoid titles that look like this:

  • Fix build.
  • Fix bug.
  • Add patch.

These don’t even attempt to describe what build, bug, or patch it is we’re dealing with. A little extra detail on what part of the build was fixed, which bug was squashed, or what patch was added can go a long way to establishing better communication and collaboration with your colleagues. It level-sets and gets folks on the same page.

PR titles are traditionally written in imperative tense. They’re a one-line summary of the entire PR, and they should describe what is being done by the PR.

Here are some good examples:

  • Support custom srcset attributes in NgOptimizedImage
  • Default image config to 75% image quality
  • Add explicit selectors for all built-in ControlValueAccessors

Avoid long PRs

A large PR means a huge description, and no one wants to review hundreds or thousands of lines of code, sometimes just to end-up dismissing the whole thing!

Instead, you could:

  • communicate with your team through Issues,
  • make a plan,
  • break down the problem into smaller pieces, or
  • work on each piece separately with its own PR.

Isn’t it much cleaner now?

Provide details in the PR body

Unlike the PR title, the body is the place for all the details, including:

  • Why is the PR being done?
  • Why is this the best approach?
  • Any shortcomings to the approach, and ideas to solve them if possible
  • The bug or ticket number, benchmark results, etc.

Reporting bugs

Bug reports are one of the most important aspects of any project. And all great projects are built on user feedback. Usually, even after countless tests, it’s the users that find most bugs. Users are also great idealists, and sometimes they have feature ideas; please listen to them!

For technical projects, all of this stuff is done by reporting issues. A well-written issue is easy for another developer to find and respond to.

For example, most big projects come with a template:

 <!-- Modified from angular-translate/angular-translate -->
 ### Subject of the issue
 Describe your issue here.

 ### Your environment
 * version of angular-translate
 * version of angular
 * which browser and its version

 ### Steps to reproduce
 Tell us how to reproduce this issue.

 ### Expected behavior
 Tell us what should happen.

 ### Actual behavior
 Tell us what happens instead.

Gather screenshots

Capture the issue using your system’s screen-shooting utility.

If it’s a screenshot of a CLI program, make sure that the text is clear. If it’s a UI program, make sure the screenshot captures the right elements and states.

You may need to capture an actual interaction to demonstrate the issue. If that’s the case, try to record GIFs using a screen-recording tool.

How to reproduce the problem

It’s much easier for programmers to solve a bug when it’s live on their computer. That’s why a good commit should come with the steps to precisely reproduce the problem.

Here’s an example:

Update: you can actually reproduce this error with objects:

 ```html
 <div *ngFor="let value of objs; let i = index">
   <input [ngModel]="objs[i].v" (ngModelChange)="setObj(i, $event)" />
 </div>
 ```

 ```js
 export class OneComponent {
   obj = {v: '0'};
   objs = [this.obj, this.obj, this.obj, this.obj];
 ​
  setObj(i: number, value: string) {
     this.objs[i] = {v: value};
  }
 }
 ```

 The bug is reproducible as long as the trackBy function returns the same value for any two entries in the array. So weird behavior can occur with any duplicate values.

Suggest a cause

You’re the one who caught the bug, so maybe you can suggest some potential causes for why it’s there. Maybe the bug only happens after you encounter a certain event, or maybe it only happens on mobile.

It also can’t hurt to explore the codebase, and maybe identify what’s causing the problem. Then, your Issue will be closed much quicker and you’re likely to be assigned to the related PR.

Communicating with clients

You may work as a solo freelancer, or perhaps you’re the lead developer on a small team. In either case, let’s say you’re responsible for interfacing with clients on a project. 

Now, the programmer stereotype is that we’re poor communicators. We’ve been known to use overly technical jargon, tell others what is and is not possible, and even get defensive when someone questions our approach.

So, how do we mitigate that stereotype? Ask clients what they want, and always listen to their feedback. Here’s how to do that.

Ask the right questions

Start by making sure that you and the client are on the same page:

  • Who is your target audience?
  • What is the goal of the site?
  • Who is your closest competitor and what are they doing right?

Asking questions is also a good way to write positively, particularly in situations when you disagree with a client’s feedback or decision. Asking questions forces that person to support their own claims rather than you attacking them by defending your own position:

  • Are you OK with that even if it comes with an additional performance cost?
  • Does moving the component help us better accomplish our objective?
  • Great, who is responsible to maintain that after launch? 
  • Do you know offhand if the contrast between those two colors passes WCAG AA standards?

Questions are a lot more innocent and promote curiosity over animosity.

Sell yourself

If you’re making a pitch to a prospective client, you’re going to need to convince them to hire you. Why should the client choose you? It’s important to specify the following:

  • Who you are
  • What you do
  • Why you’re a good fit for the job
  • Links to relevant work you’ve done

And once you get the job and need to write up a contract, remember that there’s no content more intimidating than a bunch of legalese. Even though it’s written for design projects, the Contract Killer can be a nice starting point for writing something much friendlier.

Your attention to detail could be the difference between you and another developer trying to win the same project. In my experience, clients will just as easily hire a develop they think they will enjoy working with than the one who is technically the most competent or experienced for the job.

Writing microcopy

Microcopy is the art of writing user-friendly UI messages, such as errors. I’ll bet there have been times where you as a developer had to write error messages because they were put on the backburner all the way to launch time.

That may be why we sometimes see errors like this:

Error: Unexpected input (Code 693)

Errors are the last thing that you want your users to deal with. But they do happen, and there’s nothing we can do about it. Here are some tips to improve your microcopy skills.

Avoid technical jargon

Most people don’t know what a server is, while 100% of programmers do. That’s why it’s not unusual to see uncommon terms written in an error message, like API or “timeout execution.”

Unless you’re dealing with a technical client or user base, It’s likely that most of your users didn’t take a computer science course, and don’t know how the Internet works, and why a particular thing doesn’’t work. Hence, the error.

Therefore, a good error message shouldn’t explain why something went wrong, because such explanations might require using scary technical terms. That’s why it’s very important to avoid using technical jargon.

Never blame the user

Imagine this: I’m trying to log into your platform. So I open my browser, visit your website, and enter my details. Then I’m told: “Your email/password is incorrect.”

Even though it seems dramatic to think that this message is hostile, it subconsciously makes me feel stupid. Microcopy says that it’s never okay to blame the user. Try changing your message to something less finger-pointy, like this this example adapted from Mailchimp’s login: “Sorry, that email-password combination isn’t right. We can help you recover your account.”

I’d also like to add the importance of avoiding ALL CAPS and exclamation points! Sure, they can be used to convey excitement, but in microcopy they create a sense of hostility towards the user.

Don’t overwhelm the user

Using humor in your microcopy is a good idea! It can lighten up the mood, and it’s an easy way to curb the negativity caused by even the worst errors.

But if you don’t use it perfectly, it can come across as condescending and insulting to the user. That’s just a big risk to take.

Mailchimp says it well:

[D]on’t go out of your way to make a joke — forced humor can be worse than none at all. If you’re unsure, keep a straight face.

(Emphasis mine)

Writing accessible markup

We could easily spend an entire article about accessibility and how it relates to technical writing. Heck, accessibility is often included in content style guides, including those for Microsoft and Mailchimp.

You’re a developer and probably already know so much about accessibility. You may even be one of the more diligent developers that makes accessibility a core part of your workflow. Still, it’s incredible how often accessibility considerations are put on the back burner, no matter how important we all know it is to make accessible online experiences that are inclusive of all abilities.

So, if you find yourself implementing someone else’s copywriting into your code, writing documentation for other developers, or even writing UI copy yourself, be mindful of some fundamental accessibility best practices, as they round out all the other advice for technical writing.

Things like:

Andy Bell offers some relatively small things you can do to make content more accessible, and it’s worth your time checking them out. And, just for kicks, John Rhea shows off some neat editing tricks that are possible when we’re working with semantic HTML elements.

Conclusion

Those were six ways that demonstrate how technical writing and development coincide. While the examples and advice may not be rocket science, I hope that you found them useful, whether it’s collaborating with other developers, maintaining your own work, having to write your own copy in a pinch, or even drafting a project proposal, among other things.

The bottom line: sharpening your writing skills and putting a little extra effort into your writing can actually make you a better developer.

Technical writing resources

If you’re interested in technical writing:

If you’re interested in copywriting:

If you’re interested in microcopy:

If you’re interested in using a professional style guide to improve your writing:

If you’re interested in writing for accessibility:


Technical Writing for Developers originally published on CSS-Tricks. You should get the newsletter.