How to build your first Sketch plugin

Sketch plugins open up so many opportunities for creative and new ideas. As you probably have found, plugins can do a wide variety of things such as automate existing features, provide new interfaces, get data from external locations, and more! Let’s dive into some of the very basics of plugin development.

The only thing you need for this tutorial is copy of Sketch. A little knowledge of Javascript won’t hurt either, but if not, that’s ok, I’ll be describing each step along the way. First, you will learn how to use Sketch’s script panel. By the end, you will have made a plugin that will create some awesome shapes and be well on your way to building plugins of your own.

The Script Panel

Getting started is super simple. Open up Sketch and navigate to Plugins > Run Script...

This is the script panel. It’s a little space to test your code quickly. Inside the panel you’ll find some example code already written for you.

Here is that code in a little nicer format in case you need it again.

console.log('This is an example Sketch script.')
var sketch = require('sketch')
var document = sketch.getSelectedDocument()
var selectedLayers = document.selectedLayers
var selectedCount = selectedLayers.length
if (selectedCount === 0) {
console.log('No layers are selected.')
} else {
console.log('Selected layers:');
selectedLayers.forEach(function (layer, i) {
console.log((i + 1) + '. ' + layer.name)
})
}

Sketch’s JavaScript API — Breaking down the example code

The Sketch JavaScript API is a way for us to interact with Sketch via code. Hereafter we will simply refer it to the JS API. With the JS API you can manipulate layer properties, select layers based on complex rules, export assets, and more!

To start things off let’s click the “Run” button and see what happens. This will help us get an idea what is going on.

Once you click “Run” a few lines appeared in what is called the console.

'This is an example Sketch script.'
'No layers are selected.'
Script executed in 0.119558s

The first two lines are pieces of text and the last line tells us how quickly our script took to run. You will often see messages like this in the console. Other times you will see helpful error messages in debugging your code.

To get a better sense of how what is actually going on let’s look over the example code. The first line starts with this:

console.log('This is an example Sketch script.')

This logs some text (or a String) to the console. You will often use console.log(‘something’) for checking to make sure your code behaves as expected. This is how that first line in the console was printed.

The next line is this:

var sketch = require('sketch')

This is the way you import the JS API. We will need this bit if we want to interact with Sketch.

var document = sketch.getSelectedDocument()

getSelectedDocument() is a method defined on our object sketch. It returns the selected document or null if there are no documents selected.

var selectedLayers = document.selectedLayers
var selectedCount = selectedLayers.length

Here we get the selected layers from the selected document. This is a very, very common pattern so be sure to remember that to get the selected layers you will need to do so through the document object. The length property gets the number of layers that are selected.

*One small note here for those that have a bit more Javascript experience. selectedLayers isn’t an array but rather a Selection class. If you want to get an array of the layers selected you can do so by using the Selection class’ layer property:

document.selectedLayers[0] // This doesn't work
document.selectedLayers.layers[0] // This does

Last but not least, is checking to see if there are any layers selected and if so, logging the names of the layers.

if (selectedCount === 0) {
console.log('No layers are selected.')
} else {
console.log('Selected layers:');
selectedLayers.forEach(function (layer, i) {
console.log((i + 1) + '. ' + layer.name)
})
}

First, we check if the number of layers is 0. If true, then we log the text “No layers are selected.” If the number of layers isn’t 0, we then log the text “Selected layers:” to prefix the list of layer names that we are going to log.

After that, for each layer we log the layer indexand layer name. More specifically, we use the forEach method on selectedLayers. We pass in a function which takes two inputs: the layer and the index. Since the first layer index starts at 0 we add 1 to it so we can get the log output to look like this:

1. First Layer Name
2. Second Layer Name
etc...

Hit the Road Running

Now that you know how the code works, hit the run button in the bottom right! See if you can trigger the different console messages. If you have no layers selected, then when you run the plugin you should see the text “No layers selected” in the console. If you did have some layers selected before running the script then you will see their names reported in the console. Pretty cool huh? Ah, we are just getting started…


Drawing Shapes

Drawing shapes is one of my favorite things to do in Sketch. Some days though, even drawing rectangles is too hard. On those days I really wish Sketch could do that for me. Let’s see if we can build a plugin that helps me when I’m lacking some inspiration. This is what we will make:

Oo purty shapes 🤩

In this next part we will learn how to create an artboard, make some shape layers, group them together, and reorder them. Follow along and let’s go!

1. Import Sketch & Get Common Objects

Once you have your script panel open and ready to go, the first thing that we need to do is to import the JS API. Remember, this is partthat helps us interact with Sketch.

var sketch = require('sketch')

Then, we will need to get the current document and page. To get the current page we use a method on sketch and get the selected page as a property on document.

let document = sketch.getSelectedDocument()
let page = document.selectedPage

2. Create an Artboard

The other neat thing that you can do with the JS API is to pull out various useful Sketch objects from it. I like to think of the JS API like a utility belt which has lots of neat tools. In practice, you use it to pull out various classes from it. A class is a special type of function that organizes related functions and properties (you can learn more here). Here is how we pick out the Artboard class.

let Artboard = sketch.Artboard

Now, we can use the Artboard class — the tool we just pulled out — to create an actual instance of an Artboard:

let myArtboard = new Artboard()

and if you log myArtboard you will see that you successfully created an Artboard object!

✔️ Checkpoint

Let’s take a brief moment to see what we have written. Your code should look like this so far:

let sketch = require('sketch')
let document = sketch.getSelectedDocument()
let page = document.selectedPage
let Artboard = sketch.Artboard
let myArtboard = new Artboard()

It’s good practice to move dependencies, or the imported tools, to the top of the file. Let’s move the Artboard class to the top.

let sketch = require('sketch')
let Artboard = sketch.Artboard
let document = sketch.getSelectedDocument()
let page = document.selectedPage
let myArtboard = new Artboard()

One other thing we can check is to see what myArtboard looks like. If we add console.log(myArtboard) to the end of the script and hit “Run” the logged output should look like this:

{         
type: 'Artboard',
id: '82732522-D929-4896-B98B-950E9E1EB7A8',
frame: { x: 0, y: 0, width: 100, height: 100 },
name: 'Artboard',
selected: false,
exportFormats: [ ],
sharedStyleId: null,
layers: [ ],
flowStartPoint: false,
background: {
enabled: false,
includedInExport: true,
color: '#ffffffff'
}
}

So now that we created the artboard you might be thinking,“Well…where is it exactly?” If you look underneath your script panel you should see that nothing got added to the canvas (you can also close the panel and look around to be sure — as long as you clicked “Run” your written code will still be there for you when you open the script panel again).

See how the id parameter keeps changing?

It turns out that we will need to explicitly attach the artboard to the correct page. Otherwise, the artboard object that we made will be thrown away once the script is done running. This is why if you keep hitting the Run button over and over the unique id value of the artboard will change. Sketch is creating a brand new artboard object each time your script is executed.

To attach the artboard to the page we will need to specify a parent:

let myArtboard = new Artboard()
myArtboard.parent = page

Or if we want to be a bit more succinct about it we can pass the parent property when we initialize the artboard.

let myArtboard = new Artboard({ parent: page })
Look! There it is! A marvelous artboard

Sweet! We got our artboard on our canvas. But, our artboard is a little small. In fact, the default size is 100 by 100. Perhaps you have an idea how to make the artboard larger already. If we look back to when we logged myArtboardthen you might have noticed this property:

frame: { x: 0, y: 0, width: 100, height: 100 }

So to resize our artboard to 400px by 400px we can modify myArtboard like so:

let myArtboard = new Artboard({ parent: page })
myArtboard.frame = { x: 0, y: 0, width: 400, height: 400 }

Which we can also move into the initializer:

let myArtboard = new Artboard({
parent: page,
frame: { x: 0, y: 0, width: 400, height: 400 }
})

Nice work! By now you have clicked the Run button a few times and it’s likely that you have created a few artboards on top of one another. Or maybe you have been deleting them every time you wanted to run the code again. That is a bit tedious and something we can do programmatically! To clear everything within a page you can do this:

page.layers = []

If we add that before we create our artboard then each time you run the script the previously made artboards will be removed.

So if we put everything together so far we should have something like this:

let sketch = require('sketch')
let Artboard = sketch.Artboard
let document = sketch.getSelectedDocument()
let page = document.selectedPage
page.layers = []
let myArtboard = new Artboard({
parent: page,
frame: { x: 0, y: 0, width: 400, height: 400 }
})

Great job! Now let’s add some shapes!

3. Creating a Square

Creating a square is very similar to creating an artboard. We willspecify a frame, a parent, and now a fill! Here is how we do it:

let ShapePath = sketch.ShapePath
let mySquare = new ShapePath({
parent: myArtboard,
frame: { x: 53, y: 213, width: 122, height: 122 },
style: { fills: ['#35E6C9']}
})

We are already familiar with parent and frame. style is the newest of the bunch. If we take a look at the inspector panel inside Sketch we can get an idea of what our code is doing.

When you select a layer, there are a bunch of properties like Resizing, Prototyping, Appearance, Style, Shadows, Inner Shadows, Blurs, etc. We want to add a fill which is under the Style section.

Fills can be disabled or enabled, have a blend mode, a hex value, and opacity. You can also have multiple fills as well!

Since you can have multiple fills the fill property holds a list of them (in code, a list is often called an array). That looks like this:

style: { fills: [fill1, fill2, fill3, etc..]}

But since we really only want one fill we just need to pass in one hex value.

style: { fills: ['#35E6C9’]}

Before we move on to creating other shapes I want to point out one subtle detail with frames:

let mySquare = new ShapePath({
parent: myArtboard,
frame: { x: 53, y: 213, width: 122, height: 122 }
})

Since we specified the parent artboard, the frame that we provide will be relative to the artboard that we made (it also inserts it inside the artboard as well). If we made the parent the page like we did when creating the artboard then its coordinates would be relative to the page.

✔️ Checkpoint

So, to review what our code should look like at this step, it should look like this:

let sketch = require('sketch')
let Artboard = sketch.Artboard
let document = sketch.getSelectedDocument()
let page = document.selectedPage
page.layers = []
let myArtboard = new Artboard({
parent: page,
frame: { x: 0, y: 0, width: 400, height: 400 }

})
let ShapePath = sketch.ShapePath
let mySquare = new ShapePath({
parent: myArtboard,
frame: { x: 53, y: 213, width: 122, height: 122 },
style: { fills: ['#35E6C9']}
})

Let’s move the ShapePath class up to the top to keep our dependencies together.

let sketch = require('sketch')
let Artboard = sketch.Artboard
let ShapePath = sketch.ShapePath
let document = sketch.getSelectedDocument()
let page = document.selectedPage
page.layers = []
let myArtboard = new Artboard({
parent: page,
frame: { x: 0, y: 0, width: 400, height: 400 }

})
let mySquare = new ShapePath({
parent: myArtboard,
frame: { x: 53, y: 213, width: 122, height: 122 },
style: { fills: ['#35E6C9']}
})

That is looking really great! If you click “Run” this is what should appear on the canvas:

That’s a nice square there.

*Note: If your square has a border around it then its likely that your Default Layer Style is causing this. To ensure that a border won’t be drawn you will need to pass in an empty array for borders like so. This will likely be fixed in later versions of the Sketch API.

style: { fills: ['#35E6C9’], borders: []}

4. Creating Other Shapes

Triangles

Creating other shapes like triangles, pentagons, and circles are really similar to drawing rectangles. We need to specify a parent, a frame, and a fill (well a style that contains a fill). The only difference now is that we need to specify a ShapeType:

let myTriangle= new ShapePath({
shapeType: ShapePath.ShapeType.Triangle,
parent: myArtboard,
frame: { x: 84, y: 62, width: 98, height: 80 },
style: { fills: ['#00d5ffb3']}
})

ShapePath contains an object called ShapeType. If you log ShapePath.ShapeType you’ll find a list of all the different types of shapes you can create. Here we want to use the Triangle option.

Also, you might have noticed that there is a funny looking Hex code here. Normally Hex codes are 6-digits and correspond to RGB values (two digits for each color channel). 8-Digit hex codes have some extra bit of information and use the last two digits to encode opacity. Specifically, the b3 part of the string corresponds to 70% opacity.

*If you are looking to learn more about hex codes then check out these resources:

Pentagons

Now that you know how to change the shapeType how might you be able to create a pentagon? The trick is to use the Polygon option.

let myHex = new ShapePath({
shapeType: ShapePath.ShapeType.Polygon,
parent: myArtboard,
frame: { x: 218, y: 142, width: 120, height: 120 },
style: { fills: ['#FF00B366']}
})

The default number of sides of a polygon is 5. Unfortunately, you can’t change the number of sides with the JS API just yet so we are stuck with hexagons for now.

Circles

Circles, of course, are created with the OvalShapeType. Hah! You thought it was going to be Circle, eh? I made the same mistake when writing this post out 🙃. In hindsight it makes sense though because ovals are more generic than circles. To create a circle rather than an oval we just make the width and height the same.

let myCircle = new ShapePath({
shapeType: ShapePath.ShapeType.Oval,
parent: myArtboard,
frame: { x: 106, y: 102, width: 172, height: 172 },
style: { borders: ['#0006FF66'] }
})

Notice that we specify just the border and no fill. This will create a 1px border. To specify a border width we will need to change things a little. Rather than just specifying a hex value for the border properties we also need to pass in the thickness.

color: '#0006FF66',
thickness: 16

Here is how we add those extra properties:

let myCircle = new ShapePath({
shapeType: ShapePath.ShapeType.Oval,
parent: myArtboard,
frame: { x: 106, y: 102, width: 172, height: 172 },
style: { borders: [
{
color: '#0006FF66',
thickness: 16
}
]}
})

✔️ Final Checkpoint

We have put everything together now! You can check your code below:

You did it! Congrats!!

If you want to save your script as a plugin you can do so with the “Save Script As Plugin” button.

A quick word of caution though before you do so. Because we are resetting all the layer contents of our document each time with page.layers = [] (on line 8) it can be pretty destructive to any documents that have actual work in them. As such, you might want to remove that line before converting your script to a plugin.

One you do save your script as a plugin it will appear under Plugins > Your Plugin Name > Your Plugin Name . We’ll cover how to edit this saved plugin in another article. For now, great work on making your first plugin! The next section covers some additional concepts that are extremely helpful when managing created layers.


Additional Topics

Reordering Layers

Often times you want to put your shapes in exactly the right order. There are a few ways to go about doing this.

  • Move the layer to the front or back
layer.moveToFront()
layer.moveToBack()
  • Move a layer forward/backward
layer.moveForward()
layer.moveBackward()
  • Specifying the index
let index = layer.index
//the layers current position
layer.index = 2
// assigning the index to a particular position

Note that the layer at the back of the parent (visually) will be layer 0. The layer at the front will be layer n -1 where n is the total number of layers in the parent.

Grouping Layers

Grouping layers will feel really similar to setting up an artboard. First we have to pull Group out like so:

let Group = require('sketch').Group

Just like creating an artboard, we create a new group like this:

var group = new Group({
name: 'my name',
layers: [myLayer1, myLayer2, etc..],
parent: myArtboard
})

Note that we don’t need to specify a frame here. The layers inside the group will determine the frame if we use a special little method called adjustToFit()

let myGroup = new Group({
name: "My Group",
layers: [mySquare, myTriangle, myHex, myCircle],
parent: myArtboard
})
myGroup.adjustToFit()

However, if you attempt to add all the layers to a group at the end of the script that we have been working on you might notice some funny business.

Woah, its almost like we made a “Symbols” feature here.

Honestly, I’m not entirely sure what is going on here but it seems related to the fact that the layers have more than one parent (one to the artboard and other to the group). To fix this, ensure that the layer you’re adding to a group doesn’t already have a parent. If it does, you’ll need to remove it from its existing parent first using remove(), then adding it to it’s new parent. (This is actually a bug)

let myLayers = [mySquare, myTriangle, myHex, myCircle]
// remove the parent for each layer
myLayers.forEach(layer => layer.remove())
let myGroup = new Group({
name: "My Group",
parent: myArtboard,
layers: myLayers
})
myGroup.adjustToFit()

Next Steps

Congratulations! 🎉 You have accomplished a lot so far! Now you might be wondering how to take it a step further. Here are a few suggestions:

  • See what cool art you can make. If creative coding is something that you want to dive into then I would highly recommend Processing, a language dedicated to making graphics and animations programatically.(Here is a piece I made for this article)
Link to code >
  • Learn more about the Sketch JS API. This is how I learned what functions and properties are available with each of the objects.
  • Get involved in the community. Lots of designers and developers are asking questions and sharing code over at the Sketchplugins forum and on Spectrum.

Lastly, if you have an idea for a plugin but just aren’t sure how to write it, share it in the comments below and I can point you in the right direction.If enough people have similar questions, I’ll write a blog post about it!

Until next time! 💎