Skip to main content

Accessing the Document

Let's look at some of the basic elements of writing a plugin. This section provides a brief overview of some of the APIs you are most likely to use. Check out the reference to see the full extent of the Figma Plugin API.

Plugins can access layers in the document (often referred to as nodes) in order to read and/or modify them. Two ways to get access to nodes are:

  • Reading the current selection, and
  • Performing a traversal starting from the root of the document

Getting the current selection

Most often, a plugin will do something with whatever the user has currently selected. Each page stores its selection independently. You can obtain the selection of the current page via figma.currentPage.selection which returns a ReadonlyArray<BaseNode>.

This short plugin makes the current selection half as transparent, then closes:

Get current selection
for (const node of figma.currentPage.selection) {
if ("opacity" in node) {
node.opacity *= 0.5
}
}
figma.closePlugin()

If you just want to work with one of the selected layers (common when testing), you can use figma.currentPage.selection[0]. Just keep in mind, there are always three situations your plugin needs to handle:

  • No layer is selected
  • A single layer is selected
  • Multiple layers are selected
info

Users can only select one node at a time in Dev Mode, and thus figma.currentPage.selection will only return one object in Dev Mode.

Learn more in the Working in Dev Mode guide →

Optimized document traversal

Another common plugin operation is traversing through a document. In order to search through an entire document, a plugin will typically begin its traversal at either figma.currentPage or figma.root.

caution

Carefully consider if you need to start your traversal at figma.root. This means your plugin will search the entire document, and users can create really large Figma files with many pages, which could result in your plugin taking a long time to run and freezing the UI for users. And if plugin modifies nodes outside the current page, user may not get any immediate visual feedback when running your plugin, since they will only be looking at the current page.

If your plugin does not need access to invisible nodes and their descendants inside instances, set figma.skipInvisibleInstanceChildren to true to speed up document traversal by many orders of magnitude.

Skip invisible instance children
// Skip over invisible nodes and their descendants inside instances
// for faster performance.
figma.skipInvisibleInstanceChildren = true

If you only need to find nodes by type, use node.findAllWithCriteria, as it is much faster than node.findOne and node.findAll.

findAllWithCriteria
// Finds all component and component set nodes
const nodes = node.findAllWithCriteria({
types: ['COMPONENT', 'COMPONENT_SET']
})

Node types

Each node has a type field which indicates its type, which is helpful if your plugin only needs to operate on certain types of nodes. For example, a spellchecking plugin is likely to only care about nodes where node.type === "TEXT".

Nodes of a given type have a particular set of fields that your plugin can read and write. For example, rectangle nodes (nodes with "RECTANGLE") have a .cornerRadius field, but not a .constraints field. On the other hand, frames (nodes with type "FRAME") .constraints field, but not a .cornerRadius field.

caution

In order to build a plugin that doesn't crash, you should always think about how to handle unexpected node types. Even if your plugin only cares about certain node nodes, users may try to run your plugin with any node type. Ensure your plugin provides feedback to the user if they try to use it on a node type you don't support. You can do this using the figma.notify() function.

Full document traversal

In order to search through an entire document, without any node-based filtering, you can use the node.findOne() and node.findAll() functions. Again, carefully consider if your plugin needs to do this as it could lead to performance issues for your plugin in large files.

Built-in traversal helpers
// Finds the first text node with more than 100 characters
const node = node.findOne(node => {
return node.type === "TEXT" && node.characters.length > 100
})

// Finds all empty frame nodes
const nodes = node.findAll(node => {
return node.type === "FRAME" && node.children.length === 0
})

In general, if you want to have full control over how you traverse the document, you will have to write a recursive function.

Custom traversal
// This plugin counts the number of layers, ignoring instance sublayers,
// in the document
let count = 0
function traverse(node) {
if ("children" in node) {
count++
if (node.type !== "INSTANCE") {
for (const child of node.children) {
traverse(child)
}
}
}
}
traverse(figma.root) // start the traversal at the root
alert(count)
figma.closePlugin()
info

Plugins running in Dev Mode or FigJam can only access the current page.

Learn more in the Working in Dev Mode → and Working in FigJam → guides.