Master Sketch Plugin Development: From Bundle Structure to Advanced UI Panels
This guide walks you through Sketch plugin architecture, manifest configuration, command definitions, Actions and JavaScript APIs, CocoaScript bridging, UI panel creation with AppKit, sidebar navigation, debugging techniques, logging methods, and using SketchTool for automation, providing practical code examples for each step.
Sketch Plugin Structure
A Sketch plugin is a collection of scripts that define one or more commands . It is packaged as a .sketchplugin folder, which is an OS X bundle with a standardized directory hierarchy.
Bundle Folder Layout
mrwalker.sketchplugin
Contents/
Sketch/
manifest.json
shared.js
Select Circles.cocoascript
Select Rectangles.cocoascript
Resources/
Screenshot.png
Icon.pngThe manifest.json file is the core metadata descriptor. It defines the plugin name, description, author, version, identifier, compatible Sketch version, commands, and menu items.
{
"name": "Select Shapes",
"description": "Plugins to select and deselect shapes",
"author": "Joe Bloggs",
"homepage": "https://github.com/example/sketchplugins",
"version": "1.0",
"identifier": "com.example.sketch.shape-plugins",
"compatibleVersion": "3",
"bundleVersion": 1,
"commands": [
{"name": "All", "identifier": "all", "shortcut": "ctrl shift a", "script": "shared.js", "handler": "selectAll"},
{"name": "Circles", "identifier": "circles", "script": "Select Circles.cocoascript"},
{"name": "Rectangles", "identifier": "rectangles", "script": "Select Rectangles.cocoascript"}
],
"menu": {"items": ["all", "circles", "rectangles"]}
}Command Definition
script: script file implementing the command. handler: function name invoked by Sketch (defaults to onRun if omitted). shortcut: keyboard shortcut. name: label shown in the Plugins menu. identifier: unique reverse‑DNS string (e.g., com.example.myplugin).
Menu Section
Sketch reads the menu array in the manifest to order commands in the Plugins menu.
Sketch Actions API
The Actions API lets plugins listen for user actions such as OpenDocument, CloseDocument, or SelectionChanged. Handlers are declared in manifest.json under a handlers.actions object.
"commands": [
{
"script": "my-action-listener.js",
"name": "My Action Listener",
"handlers": {"actions": {"OpenDocument": "onOpenDocument"}},
"identifier": "my-action-listener"
}
]Example handler:
export function onOpenDocument(context) {
context.actionContext.document.showMessage('Document Opened')
}Sketch JavaScript API
Three development approaches exist:
Pure CocoaScript.
Hybrid JavaScript + CocoaScript.
Objective‑C + AppKit.
Sketch recommends the JavaScript API, which wraps native Sketch objects ( Document, Artboard, Group, Layer) for easier manipulation. Low‑level features may still require CocoaScript or Objective‑C.
CocoaScript
CocoaScript bridges JavaScript to the Objective‑C runtime, allowing calls to macOS frameworks via Apple’s JavaScriptCore engine.
// Objective‑C style
NSString *value = [command valueForKey:kAutoresizingMask onLayer:currentLayer];
// CocoaScript equivalent
const value = command.valueForKey_onLayer(kAutoresizingMask, currentLayer);
// Showing a dialog via NSApplication
const app = NSApplication.sharedApplication();
app.displayDialog_withTitle('Choose a location…', 'Export');Objective‑C Classes
Sketch’s internal classes (prefixed with MS) are exposed to JavaScript via the Bridge. Introspection methods include properties(), instanceMethods(), and classMethods().
String(context.document.class()) // => "MSDocument"
const mocha = context.document.class().mocha()
mocha.properties() // array of MSDocument propertiesContext Object
When a command runs, Sketch passes a context object containing:
command : MSPluginCommand instance.
document : MSDocument instance.
plugin : MSPluginBundle instance.
scriptPath : absolute path of the script.
scriptURL : NSURL of the script.
selection : NSArray of selected MSLayer objects.
Development Setup
Useful defaults for a smoother development cycle:
Disable automatic safe mode after a crash:
defaults write com.bohemiancoding.sketch3 disableAutomaticSafeMode trueEnable script reload on each run:
defaults write com.bohemiancoding.sketch3 AlwaysReloadScript -bool YESEnable WebView debugging:
defaults write com.bohemiancoding.sketch3 WebKitDeveloperExtras -bool YESCreating an Auxiliary Panel
Use NSPanel (a subclass of NSWindow) for a floating panel and NSVisualEffectView for a blurred background.
const panelWidth = 80;
const panelHeight = 240;
const panel = NSPanel.alloc().init();
panel.setFrame_display(NSMakeRect(0, 0, panelWidth, panelHeight), true);
panel.setStyleMask(NSTexturedBackgroundWindowMask | NSTitledWindowMask | NSClosableWindowMask | NSFullSizeContentViewWindowMask);
panel.setBackgroundColor(NSColor.whiteColor());
panel.title = "";
panel.titlebarAppearsTransparent = true;
panel.center();
panel.makeKeyAndOrderFront(null);
panel.setLevel(NSFloatingWindowLevel);
COScript.currentCOScript().setShouldKeepAround(true);
panel.standardWindowButton(NSWindowMiniaturizeButton).setHidden(true);
panel.standardWindowButton(NSWindowZoomButton).setHidden(true);Add a blurred background:
const vibrancy = NSVisualEffectView.alloc().initWithFrame(NSMakeRect(0, 0, panelWidth, panelHeight));
vibrancy.setAppearance(NSAppearance.appearanceNamed(NSAppearanceNameVibrantLight));
vibrancy.setBlendingMode(NSVisualEffectBlendingModeBehindWindow);
panel.contentView().addSubview(vibrancy);Insert a WKWebView to render HTML content:
const wkwebviewConfig = WKWebViewConfiguration.alloc().init();
const webView = WKWebView.alloc().initWithFrame_configuration(CGRectMake(0, 0, panelWidth, panelWidth), wkwebviewConfig);
panel.contentView().addSubview(webView);
webView.loadFileURL_allowingReadAccessToURL(NSURL.URLWithString(url), NSURL.URLWithString('file:///'));Sidebar Navigation Development
Typical AppKit classes for a toolbar include NSStackView, NSBox, NSImageView, and NSButton.
const toolbar = NSStackView.alloc().initWithFrame(NSMakeRect(0, 0, 40, 400));
toolbar.identifier = SidePanelIdentifier;
toolbar.setSpacing(8);
toolbar.setFlipped(true);
toolbar.setBackgroundColor(NSColor.windowBackgroundColor());
toolbar.orientation = 1;
// Add logo image view
const Logo = createImageView(NSMakeRect(0, 0, 40, 30), 'logo', NSMakeSize(40, 28));
toolbar.addSubview(Logo);
// Insert toolbar into the main window's stage view
const contentView = context.document.documentWindow().contentView();
const stageView = contentView.subviews().objectAtIndex(0);
const views = stageView.subviews();
// Merge toolbar with existing views (simplified example)
stageView.subviews = views.concat([toolbar]);
stageView.adjustSubviews();Full source code is available at https://github.com/o2team/sketch-plugin-boilerplate.
Debugging
Sketch creates a JavaScript context for each plugin. Enable Safari’s Web Inspector via
Developer → YourMachine → Automatically Show Web Inspector for JSContextsand pause on connection to inspect variables and set breakpoints.
Logging
Use console.log or console.error in scripts. Logs can be viewed through:
Console.app (filter for Sketch).
File ~/Library/Logs/com.bohemiancoding.sketch3/Plugin Output.log. skpm log (or skpm log -f for streaming).
If using skpm, install sketch-dev-tools to view logs in the UI.
SketchTool (CLI)
sketchtoolis a command‑line utility bundled with Sketch for exporting artboards, layers, pages, checking documents, dumping JSON data, and running plugins.
alias sketchtool="/Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool"
sketchtool -h # show help
sketchtool export artboards path/to/document.sketch # export artboards
sketchtool dump path/to/document.sketch # export JSON data
sketchtool metadata path/to/document.sketch # view metadata
sketchtool run [Plugin path] # run a pluginNote: SketchTool requires macOS 10.11 or later.
Aotu Lab
Aotu Lab, founded in October 2015, is a front-end engineering team serving multi-platform products. The articles in this public account are intended to share and discuss technology, reflecting only the personal views of Aotu Lab members and not the official stance of JD.com Technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
