DEV Community

seya
seya

Posted on

Create your own Figma code generator!

I recently released a plugin that generates React code from Figma.

Figma to React Component

Source code for this plugin is available in the repository below.
If you find it interesting, please Star it! ⭐️

https://github.com/kazuyaseki/figma-to-react

The above repository is for generating React, but I'm sure there are other formats besides React that write View in various ways such as Vue, Flutter or whatever you like.

So, in this article, I will explain how to extend and edit the code in the above repository to make your own Figma code generator.

※ By the way, feel free to publish plugin you create based on the code from above repository to the Figma Community. I will not complain anything!

Introduction

When you want to make your own Figma code generator, I think there are three main patterns that you might want to make.

  1. to generate code in another View format
  2. to add component descriptions
  3. to generate CSS in another format

If you want to implement any of the above, I'll explain in the following order to show you what you'd want to know.

  • Architecture of the Figma Plugin
    • I'll explain what kind of environment the Figma Plugin is running in.
  • Explanation of intermediate expressions - buildTagTree function
    • This plugin generates a tree of Figma nodes as an intermediate representation to make it easier to write the final code strings.
  • Rewriting the Tree for each component - modifyTreeForComponent
  • Generate a string for the code part - buildCode
  • Output CSS string - buildCssString

Figma Plugin Architecture

The Figma Plugin is divided into two execution environments: the Figma sandbox environment and the environment in which the plugin runs.

In the former, you may do things like accessing "figma" objects, getting the location and style information of the actual objects on Figma, and creating new objects.
The latter is used to create the UI of the plugin itself, and can be developed in much the same way as normal web frontend development. It is also possible to send requests to the outside world.

These two environments exchange data through the message function.

Alt Text

For example, in Figma to React, the process is as follows.

  • Create an object on Figma in the Sandbox environment and generate a code string.
  • Pass the generated string to the UI thread with the figma.ui.postMessage function.
  • Display the received message in the plugin.

Figma plugin development is a process of using two environments to develop a plugin. Both environments have their own limitations, which can be a bit troublesome, but basically, the limitations are not that bothering.

If you want to know more about it, please refer to the following document.
https://www.figma.com/plugin-docs/how-plugins-run/

Intermediate Expressions Explained - buildTagTree function

type buildTagTree = (node: SceneNode): Tag | null
Enter fullscreen mode Exit fullscreen mode

https://github.com/kazuyaseki/figma-to-react/blob/main/src/buildTagTree.ts

This function converts the specified Node into a tree object that can be easily formed later.
More properties may be added in the future, but the current type of Tag is as follows.

type Property = {
  name: string
  value: string
  notStringValue?: boolean
}

export type Tag = {
  name: string
  node: SceneNode
  isImg: boolean
  isText: boolean
  textCharacters: string | null
  properties: Property[], css: CSSData
  css: CSSData
  children: Tag[] }
}
Enter fullscreen mode Exit fullscreen mode

In the case of a text node, it contains the name and textCharacters of the text node. children contains child nodes of the Tag type, so it is nested.

In addition, the original node is also included, so you can do anything you want.

Rewrite Tree for each component - modifyTreeForComponent

https://github.com/kazuyaseki/figma-to-react/blob/main/src/modifyTreeForComponent.ts

type modifyTreeForComponent = (tree: Tag, _figma: PluginAPI): Tag
Enter fullscreen mode Exit fullscreen mode

This function recursively looks at the Tag and rewrites it if it matches the configuration of the specified component.

For example, in the codebase, the configuration for the component Spacer is written as follows.

{
    name: 'Spacer',
    matcher: (node: SceneNode) => {
      return node.name === 'Spacer' && (! ('children' in node) || node.children.length === 0)
    },
    modifyFunc: (tag: Tag) => {
      if (tag.node.width > tag.node.height) {
        tag.properties.push({ name: 'height', value: tag.node.height.toString(), notStringValue: true })
      } else {
        tag.properties.push({ name: 'width', value: tag.node.width.toString(), notStringValue: true })
      }

      tag.isComponent = true
      return tag
    }
  }
Enter fullscreen mode Exit fullscreen mode

In the matcher section, you can specify the condition that a node of the tag is a Spacer component if it has any property.

The modifyFunc section specifies how to modify the tag.
For example, in this case, if the width of the node is greater than the height, the width property is passed so that it can be drawn as Props.

If you want to add your own component definition like this, you can add it here.

Generate a string of code parts - buildCode

https://github.com/kazuyaseki/figma-to-react/blob/main/src/buildCode.ts

This is the part that generates the React code.
If you want to create something for other ways of writing View, please try your best to create something similar to this.

function buildJsxString(tag: Tag, cssStyle: CssStyle, level: number) {
  const spaceString = buildSpaces(4, level)
  const hasChildren = tag.children.length > 0

  const tagName = getTagName(tag, cssStyle)
  const className = getClassName(tag, cssStyle)
  const properties = tag.properties.map(buildPropertyString).join('')

  const openingTag = `${spaceString}<${tagName}${className}${properties}${hasChildren || tag.isText ? `` : '/'}>``
  const childTags = buildChildTagsString(tag, cssStyle, level)
  const closingTag = hasChildren || tag.isText ? `${!tag.isText ? '\n' + spaceString : ''}</${tagName}>` : ''

  return openingTag + childTags + closingTag
}

export function buildCode(tag: Tag, css: CssStyle): string {
  return `const ${tag.name.replace(/\s/g, '')}: React.VFC = () => {
  return (
${buildJsxString(tag, css, 0)}
  )
}`
}
Enter fullscreen mode Exit fullscreen mode

Output CSS string - buildCssString

https://github.com/kazuyaseki/figma-to-react/blob/main/src/buildCssString.ts

The last part is to generate CSS.
In this example, it builds an array of CSS by recursively traversing the Tags, then it generates the string.
If you want to support other CSS formats, please try your best to tweak this area.

export function buildCssString(tag: Tag, cssStyle: CssStyle): string {
  const cssArray = buildArray(tag, [])
  let codeStr = ''

  cssArray.forEach((cssData) => {
    const cssStr =
      cssStyle === 'styled-components'
        ? `const ${cssData.className.replace(/\s/g, '')} = styled.div\`
${cssData.properties.map((property) => `  ${property.name}: ${property.value};`).join('\n')}
\`\n`
        : `.${kebabize(cssData.className)} {
${cssData.properties.map((property) => `  ${property.name}: ${property.value};`).join('\n')}
}\n`

    codeStr += cssStr
  })

  return codeStr
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

This is a brief description of the Figma code generator.
I hope the quality of the code is good enough to support you in creating your own Figma code generator.

If you think it looks interesting, please try to make it. Have a great Figma life!

Top comments (0)