New in Sketch 2025.3
Model Context Protocol
Sketch now ships with a built-in MCP server that enables connected AI clients to write and execute JavaScript code directly in Sketch.
This server transforms Sketch into an interactive AI-driven REPL environment for SketchAPI scripts. Whether you want AI as a sidekick for writing and troubleshooting Sketch plugins, or as a fully autonomous agent that can iterate on code independently, the MCP server enables both workflows.
Image Background Removal
Our new on-device image background removal feature powered by Apple’s Vision framework is now available via the asynchronous JavaScript API:
anImageLayer.removeBackground((error) => {
if (error) {
console.error(`Background removal failed: ${error}`)
}
})
// or, in case of multiple image layers:
const { Image } = require('sketch')
Image.removeBackgroundFromLayers(imageLayers, (error) => {
if (error) {
console.error(
`Background removal failed for ${imageLayers.length} images: ${error}`,
)
}
})
Both methods accept an optional people flag that switches the underlying ML model to one optimized for images with people:
anImageLayer.removeBackground({ people: true }, (error) => {
// ...
})
Image.removeBackgroundFromLayers(imageLayers, { people: true }, (error) => {
// ...
})
Wrapping Stacks
You can now control whether the contents of a particular Stack Layout should wrap when their combined height (for vertical stacks) or width (for horizontal stacks) exceeds the Stack’s fixed dimensions:
const { Group, StackLayout, FlexSizing } = require('sketch')
// for new Stacks
const stack = new Group.Frame({
stackLayout: {
direction: StackLayout.Direction.Row,
wraps: true,
},
horizontalSizing: FlexSizing.Fixed,
})
// or, for existing Stacks
container.stackLayout.wraps = true
You can also choose how the wrapped content should be aligned on the cross axis:
container.stackLayout.alignContent = StackLayout.AlignContent.End
and specify the gap between wrapped lines:
container.stackLayout.crossAxisGap = 30
Symbols
Back in Sketch 94, we introduced an option to expand Symbol instances right in the Layer List to reveal all overridable layers inside – so you could select any of them and use the Overrides panel in the Inspector to quickly change overrides for those specific layers.
As for the JS API, working with layers inside Symbol Instances has always been a challenge. Symbol Instances don’t contain any layers – their parent Symbol Sources do; in fact, instances are simply references to those Sources with additional metadata like Overrides attached. Because of those Overrides, iterating through a Symbol Source’s layers while mapping them to an actual Symbol Instance configuration is error-prone.
Looking inside Symbol Instances
To solve this problem, Sketch 2025.3 introduces SymbolInstance.expandedLayers – a read-only collection of overridable layers inside the given Symbol Instance:
symbolInstance.expandedLayers.forEach((layer) => {
const frame = layer.frame
const isHidden = layer.hidden
if (layer.type === sketch.Types.Text) {
console.log(layer.text === 'This text is overridden') // true
}
layer.layers?.forEach((grandchild) => {
// ...
})
if (layer.isNestedSymbol) {
const symbolSource = document.getSymbolMasterWithID(layer.symbolId)
// ...
}
})
Nested Symbols are represented as normal Groups with a couple of special properties: isNestedSymbol and symbolId – to help differentiate them from regular layer containers.
Modifying Symbol Instances
Overrides are still the only way to make changes to Symbol Instances. To access an instance’s overrides while iterating its expandedLayers collection, use the new helper method SymbolInstance.overridesForExpandedLayer():
symbolInstance.expandedLayers.forEach((layer) => {
findAndUpdateText(layer, symbolInstance)
})
function findAndUpdateText(layer, parentInstance) {
if (layer.hidden) return
if (layer.name === 'the one') {
const overrides = parentInstance.overridesForExpandedLayer(layer)
const textOverride = overrides.find(o => o.property === 'stringValue')
textOverride?.value = 'New text'
// (optional) dive in recursively
layer.layers?.forEach((grandchild) =>
findAndUpdateText(grandchild, parentInstance),
)
}
}
Color Variables
We’d like to give a huge thank you to Kevin Gutowski for his remarkable “What’s up with Color Variables?” post that’s been a tremendous source of inspiration for us 🙌
Color Variables (aka Swatches) are now fully represented in the JS API:
-
A new
swatchproperty has been added next to thecolorproperty on Fills, Borders, Shadows, GradientStops, Tints, Texts, and Overrides:// On individual style parts style.fills[0].swatch style.borders[0].swatch style.shadows[0].swatch // including Group Tints group.style.tint.swatch // On gradient stops style.fills[0].gradient.stops[0].swatch // On Texts textLayer.style.textSwatch // On Overrides if (override.colorOverride) { override.swatchValue override.defaultSwatchValue } -
Swatch.coloris now a writable property:swatch.color = '#ff6600ff' // Note: all colors referencing this swatch will be auto-updated to this value -
Added
Swatch.getLibrary()andSwatch.syncWithLibrary():const sourceLibraryName = swatch.getLibrary()?.name swatch.syncWithLibrary()
Color Variables 101
As a quick reminder, here’s an overview of the Color Variables API:
// A Color Variable must be added to a document before its first use: document.swatches.append({ name: 'Safety Orange', color: '#ff6600ff', }) const swatch = document.swatches[0] // Now, wherever you'd normally specify a plain color string, you may provide // a special color object that's tied to that Color Variable and will // automatically update itself when the latter changes: layer.style.fills = [ { fillType: 'Color', color: swatch.referencingColor, }, ] // Alternatively, pass a swatch object directly: layer.style.fills = [ { fillType: 'Color', swatch: swatch, }, ] // You may then go ahead and change the actual color value of this Swatch: swatch.color = '#ffffffff' // which will update all references to this Swatch throughout the document: console.log(layer.style.fills[0].color === '#ffffffff') // trueIn addition to local Color Variables, Sketch also supports Color Variables imported from Libraries:
const sketch = require('sketch') // First, get a list of all Swatches available from a given library: const document = sketch.getSelectedDocument() const library = sketch.Library.getLibraryForDocumentAtPath( 'ACME Design System.sketch', ) const allLibrarySwatches = library.getImportableSwatchReferencesForDocument(document) // Then find the one you need and `import()` it: const importable = allLibrarySwatches.find( (s) => s.name === 'Unsafety Orange', ) const importedSwatch = importable.import() // That's it: you may now use this imported Swatch like a regular one layer.style.fills = [ { fillType: 'Color', swatch: importedSwatch, }, ] // The only difference is that it has its source library information attached: console.log(importedSwatch.getLibrary()?.name === 'ACME Design System') // true // and can be updated whenever there are changes in the source library: importedSwatch.syncWithLibrary()
Other Changes and Bugfixes
- Added
StackLayout.AlignItems.Stretchto mimic the new “Stretch vertically/horizontally” option for aligning items within Stacks. - We’ve deprecated
Blur.isCustomGlassas Auto Glass mode doesn’t exist anymore in Sketch. This property will always returntruefrom now on. - Fixed an issue where text layers created via
new Text({ style: ... })would have an unexpected default border applied to them. - Fixed
SharedStyle.styleTypeto return an actual effective style type instead of a “SharedStyle” string literal. - Fixed
StackLayout.gapto accept non-integer values. - Fixed an issue where
find('Frame')andfind('Graphic')wouldn’t match any Symbol Masters.