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:
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 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
.
// 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.
// 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.
// 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.