Applies multiple annotations to different nodes in a single operation. This tool processes annotations in batches of 5 for optimal performance, making it ideal for annotating complex designs with many elements.
Parameters
The ID of the parent node containing all elements to be annotated. Used for scoping the batch operation.
Array of annotation objects to apply. Each annotation object contains: Show Annotation object properties
The ID of the specific node to annotate
The annotation text in markdown format (supports bold , italic , links, lists, code, etc.)
Optional annotation category ID
Optional ID of existing annotation to update (omit for new annotations)
Optional array of property objects, each with a type field
Response
Returns a detailed result object:
{
success : boolean ,
nodeId : string ,
annotationsApplied : number ,
annotationsFailed : number ,
totalAnnotations : number ,
completedInChunks : number ,
results : Array <{
success : boolean ,
nodeId : string ,
annotationId ?: string ,
error ?: string
}>
}
Usage
Basic batch annotation
With categories and properties
const result = await set_multiple_annotations ({
nodeId: "parent-frame-id" ,
annotations: [
{
nodeId: "button-1" ,
labelMarkdown: "**Primary CTA**: Main action button"
},
{
nodeId: "button-2" ,
labelMarkdown: "**Secondary action**: Cancel flow"
},
{
nodeId: "input-1" ,
labelMarkdown: "**Email input**: Required field, validates format"
}
]
});
console . log ( ` ${ result . annotationsApplied } of ${ result . totalAnnotations } applied` );
Batch Processing
The tool automatically processes annotations in batches of 5 to prevent Figma UI freezing. Progress updates are provided during processing:
Starting annotation process for 23 nodes. This will be processed in batches of 5...
Annotation process completed:
- 21 of 23 successfully applied
- 2 failed
- Processed in 5 batches
Nodes that failed:
- 123:456: Node not found
- 789:012: Invalid markdown format
Complete Annotation Workflow
Typical workflow for annotating a complex design:
1. Scan for annotation targets
First, identify all elements that need annotations:
// Get parent frame
const selection = await get_selection ();
const parentFrameId = selection [ 0 ]. id ;
// Find all components and key frames
const targets = await scan_nodes_by_types ({
nodeId: parentFrameId ,
types: [ "COMPONENT" , "INSTANCE" , "FRAME" ]
});
console . log ( `Found ${ targets . count } potential annotation targets` );
2. Get available categories
Retrieve annotation categories for consistent organization:
const { categories } = await get_annotations ({
nodeId: parentFrameId ,
includeCategories: true
});
// Find specific categories
const designCategory = categories . find ( c => c . name === "Design" );
const devCategory = categories . find ( c => c . name === "Development" );
3. Build annotation array
Map targets to annotations with appropriate content:
const annotations = targets . matchingNodes . map ( node => {
// Determine category based on node type
const categoryId = node . type === "COMPONENT"
? designCategory . id
: devCategory . id ;
// Generate annotation text based on node name and type
const labelMarkdown = `** ${ node . name } ** \n\n Type: ${ node . type } \n Size: ${ node . bbox . width } × ${ node . bbox . height } px` ;
return {
nodeId: node . id ,
labelMarkdown ,
categoryId
};
});
4. Apply batch annotations
Apply all annotations in one operation:
const result = await set_multiple_annotations ({
nodeId: parentFrameId ,
annotations
});
console . log ( `Applied ${ result . annotationsApplied } annotations in ${ result . completedInChunks } batches` );
if ( result . annotationsFailed > 0 ) {
console . error ( "Failed annotations:" , result . results . filter ( r => ! r . success ));
}
Converting Manual Annotations
Common use case: converting numbered/lettered annotation markers to native Figma annotations.
Scan text nodes for markers
// Get all text nodes (potential annotation markers and descriptions)
const textNodes = await scan_text_nodes ({ nodeId: frameId });
// Filter for annotation markers (short text like "1", "2", "A", "B")
const markers = textNodes . filter ( node =>
node . characters . length <= 2 &&
/ ^ [ 0-9A-Za-z ] $ / . test ( node . characters )
);
// Filter for descriptions (longer text near markers)
const descriptions = textNodes . filter ( node =>
node . characters . length > 10
);
Match markers to UI elements
// Get UI elements that are annotation targets
const uiElements = await scan_nodes_by_types ({
nodeId: frameId ,
types: [ "COMPONENT" , "INSTANCE" , "FRAME" ]
});
// Match by proximity (find closest UI element to each marker)
const annotations = markers . map ( marker => {
// Find corresponding description
const description = descriptions . find ( d =>
d . name . includes ( marker . characters ) ||
isNear ( d . bbox , marker . bbox )
);
// Find closest UI element
const target = findClosestElement ( marker . bbox , uiElements . matchingNodes );
return {
nodeId: target . id ,
labelMarkdown: description ?. characters || `Annotation ${ marker . characters } `
};
});
Apply converted annotations
const result = await set_multiple_annotations ({
nodeId: frameId ,
annotations
});
console . log ( `Converted ${ annotations . length } manual annotations to native annotations` );
Performance Considerations
Batch size : Fixed at 5 annotations per batch for optimal performance
Large operations : Annotating 50+ nodes will take time; progress updates keep you informed
Verification : Check results array for any failed annotations
Retry logic : Failed annotations can be retried individually with set_annotation
Use Cases
Design system documentation
// Annotate all components with usage guidelines
const components = await scan_nodes_by_types ({
nodeId: designSystemPageId ,
types: [ "COMPONENT" ]
});
const annotations = components . matchingNodes . map ( comp => ({
nodeId: comp . id ,
labelMarkdown: `** ${ comp . name } ** \n\n See [component docs](https://ds.example.com/ ${ comp . name . toLowerCase () } )` ,
categoryId: "documentation"
}));
await set_multiple_annotations ({ nodeId: designSystemPageId , annotations });
Development handoff
// Add implementation notes to all interactive elements
const interactiveElements = await scan_nodes_by_types ({
nodeId: screenId ,
types: [ "INSTANCE" , "COMPONENT" ]
});
const annotations = interactiveElements . matchingNodes
. filter ( node => node . name . toLowerCase (). includes ( 'button' ))
. map ( btn => ({
nodeId: btn . id ,
labelMarkdown: `**onClick**: Navigate to corresponding screen \n **State**: Supports loading, disabled` ,
categoryId: devCategoryId
}));
await set_multiple_annotations ({ nodeId: screenId , annotations });
Best Practices
Use scan tools first : Always use scan_nodes_by_types to identify targets before batch annotating
Consistent categories : Use the same category for related annotations
Rich markdown : Leverage markdown for structured, readable annotations
Check results : Always inspect the results array for failures
Logical grouping : Keep annotations in the same batch if they’re related
Related Tools
get_annotations Retrieve existing annotations and categories
set_annotation Create or update single annotation
scan_nodes_by_types Find nodes to annotate
scan_text_nodes Find text nodes (for converting manual annotations)