Annotation System Overview
Figma’s native annotation system allows you to attach notes, comments, and documentation directly to design elements. The Talk to Figma MCP provides tools to work with annotations programmatically.
Available Annotation Tools
get_annotations - Retrieve all annotations in a document or node
set_annotation - Create or update a single annotation
set_multiple_annotations - Batch create/update annotations efficiently
scan_nodes_by_types - Find potential annotation targets
Converting Manual Annotations
Many designs use manual annotation systems (numbered markers with text descriptions). Converting these to native Figma annotations improves collaboration and maintains design system integrity.
Process Overview
- Get selection and initial setup
- Scan annotation text nodes
- Scan target UI elements
- Match annotations to targets
- Apply native Figma annotations
- Verify and clean up
Step 1: Initial Setup
Get Selection
Start by identifying the frame or component containing annotations:
// Get current selection
const selection = await get_selection();
const selectedNodeId = selection[0].id;
// Get available annotation categories
const annotationData = await get_annotations({
nodeId: selectedNodeId,
includeCategories: true
});
const categories = annotationData.categories;
Figma provides default annotation categories. Understanding available categories helps you classify annotations appropriately.
Step 2: Scan Annotation Text Nodes
Identify Markers and Descriptions
Scan all text nodes to find annotation components:
// Get all text nodes
const textNodes = await scan_text_nodes({
nodeId: selectedNodeId
});
// Filter for markers and descriptions
// Markers typically have:
// - Short text content (single digit/letter)
// - Specific font styles (often bold)
// - Container with "Marker" or "Dot" in name
// - Clear naming pattern ("1", "2", "3" or "A", "B", "C")
Common Marker Patterns
Markers are usually identified by:
- Text content: Single character or number (“1”, “A”, “①”)
- Container names: “Marker”, “Annotation”, “Dot”, “Number”
- Font weight: Often bold (700+)
- Size: Typically smaller than body text
Description Patterns
Descriptions typically have:
- Longer text content
- Located near markers
- Matching numbers in layer path
- Explanatory content
Manual annotation systems vary widely. Analyze your specific design pattern to identify the correct matching logic.
Step 3: Scan Target UI Elements
Find Annotation Targets
Identify UI elements that annotations refer to:
// Scan for potential target elements
const targetNodes = await scan_nodes_by_types({
nodeId: selectedNodeId,
types: ["COMPONENT", "INSTANCE", "FRAME"]
});
Native Figma annotations can be attached to COMPONENT, INSTANCE, and FRAME nodes. Scan for these types to find valid targets.
Target Node Types
- COMPONENT - Main component definitions
- INSTANCE - Component instances
- FRAME - Frame containers
Other node types (TEXT, RECTANGLE, etc.) cannot receive native annotations directly.
Step 4: Match Annotations to Targets
Matching Strategies
Use multiple strategies in order of priority:
1. Path-Based Matching (Highest Priority)
Match based on layer hierarchy:
// Look at marker's parent container name
// Remove "Marker:" or "Annotation:" prefixes
// Find UI elements with matching parent names
function findTargetByPath(marker, targetNodes) {
// Get marker's parent path
const parentName = marker.parent.name
.replace(/^(Marker:|Annotation:)\s*/i, '');
// Find targets with matching parent
return targetNodes.find(node =>
node.parent?.name === parentName ||
node.name === parentName
);
}
When to use:
- Markers are grouped with their target elements
- Layer hierarchy follows naming conventions
- Annotations are organized in frames
2. Name-Based Matching
Match based on description content:
function findTargetByName(description, targetNodes) {
// Extract key terms from description
const keywords = extractKeywords(description.text);
// Find nodes whose names contain keywords
return targetNodes.find(node =>
keywords.some(keyword =>
node.name.toLowerCase().includes(keyword.toLowerCase())
)
);
}
When to use:
- Descriptions mention specific element names
- Form fields, buttons, and labeled components
- Semantic naming conventions are followed
3. Proximity-Based Matching (Fallback)
Match based on spatial position:
function findTargetByProximity(marker, targetNodes) {
const markerCenter = {
x: marker.x + marker.width / 2,
y: marker.y + marker.height / 2
};
// Find closest element
let closest = null;
let minDistance = Infinity;
for (const node of targetNodes) {
const nodeCenter = {
x: node.x + node.width / 2,
y: node.y + node.height / 2
};
const distance = Math.sqrt(
Math.pow(nodeCenter.x - markerCenter.x, 2) +
Math.pow(nodeCenter.y - markerCenter.y, 2)
);
if (distance < minDistance) {
minDistance = distance;
closest = node;
}
}
return closest;
}
When to use:
- Other strategies fail
- Markers positioned near their targets
- Simple spatial layouts
Proximity-based matching can be inaccurate in dense layouts. Always verify results when using this strategy.
Combined Matching Strategy
function findBestTarget(marker, description, targetNodes) {
// Try strategies in priority order
let target = findTargetByPath(marker, targetNodes);
if (!target) {
target = findTargetByName(description, targetNodes);
}
if (!target) {
target = findTargetByProximity(marker, targetNodes);
}
return target;
}
Step 5: Apply Native Annotations
Determine Category
Classify annotations based on content:
function determineCategory(descriptionText, categories) {
const text = descriptionText.toLowerCase();
// Pattern matching for categories
if (text.includes('bug') || text.includes('fix')) {
return categories.find(c => c.name === 'Bug');
}
if (text.includes('todo') || text.includes('implement')) {
return categories.find(c => c.name === 'To Do');
}
if (text.includes('note') || text.includes('info')) {
return categories.find(c => c.name === 'Note');
}
// Default category
return categories[0];
}
Batch Create Annotations
Use set_multiple_annotations for efficiency:
// Prepare annotations array
const annotationsToApply = matchedPairs.map(({ marker, description, target }) => {
const category = determineCategory(description.text, categories);
return {
nodeId: target.id,
labelMarkdown: description.text,
categoryId: category.id,
properties: determineProperties(description.text, target.type)
};
}).filter(Boolean); // Remove null entries
// Apply in batch
if (annotationsToApply.length > 0) {
await set_multiple_annotations({
nodeId: selectedNodeId,
annotations: annotationsToApply
});
}
Batch operations are significantly more efficient than creating annotations one at a time. Always use set_multiple_annotations when possible.
Annotation Properties
Determine additional properties based on context:
function determineProperties(descriptionText, targetType) {
const properties = {};
// Add priority if mentioned
if (descriptionText.includes('critical') || descriptionText.includes('urgent')) {
properties.priority = 'high';
}
// Add assignee if mentioned
const assigneeMatch = descriptionText.match(/@(\w+)/);
if (assigneeMatch) {
properties.assignee = assigneeMatch[1];
}
return properties;
}
Step 6: Verify and Clean Up
Verify Annotations
Check that annotations were created correctly:
// Get all annotations to verify
const verifyData = await get_annotations({
nodeId: selectedNodeId
});
console.log(`Created ${verifyData.annotations.length} annotations`);
Delete Legacy Markers
After successful conversion, remove manual annotation nodes:
// Collect marker and description node IDs
const nodesToDelete = [
...markerNodeIds,
...descriptionNodeIds
];
// Delete in batch
await delete_multiple_nodes({
nodeIds: nodesToDelete
});
Only delete manual annotations after verifying native annotations were created successfully. Always create a backup first.
Single Annotation Creation
For individual annotations, use set_annotation:
await set_annotation({
nodeId: "target-node-id",
labelMarkdown: "This button triggers the login action",
categoryId: "category-id" // Optional
});
Markdown Support
Annotation labels support markdown formatting:
await set_annotation({
nodeId: "node-id",
labelMarkdown: `
**Important:** This component handles authentication.
- Validates user credentials
- Shows error messages
- Redirects on success
See [documentation](https://example.com) for details.
`.trim()
});
Use markdown formatting to create rich, informative annotations with headings, lists, links, and emphasis.
Retrieving Annotations
Get All Annotations
// Get annotations for specific node
const annotations = await get_annotations({
nodeId: "frame-id"
});
// Get with categories
const fullData = await get_annotations({
nodeId: "frame-id",
includeCategories: true
});
Filter by Category
const annotations = await get_annotations({ nodeId: "frame-id" });
// Filter specific category
const bugs = annotations.annotations.filter(
a => a.categoryId === bugCategoryId
);
Best Practices
Organization
- Create annotations at the appropriate hierarchy level
- Use consistent category assignment logic
- Group related annotations together
- Use descriptive, actionable text
Content Guidelines
- Write clear, concise annotation text
- Include context and reasoning
- Link to relevant documentation
- Mention stakeholders when appropriate (using @mentions)
- Add priority indicators for critical items
Performance
- Use
set_multiple_annotations for batch operations
- Limit annotation count per frame (aim for < 20)
- Clean up resolved annotations regularly
- Use categories to organize large annotation sets
Too many annotations can clutter the design view. Focus on actionable, relevant annotations.
Common Use Cases
Design Review Annotations
// Add review comments
await set_multiple_annotations({
nodeId: "screen-id",
annotations: [
{
nodeId: "button-id",
labelMarkdown: "**Accessibility:** Add ARIA label for screen readers",
categoryId: a11yCategoryId
},
{
nodeId: "image-id",
labelMarkdown: "Replace with high-res asset @designer",
categoryId: todoCategoryId
}
]
});
Development Handoff
// Add implementation notes
await set_annotation({
nodeId: "component-id",
labelMarkdown: `
## Implementation Notes
- Use \`LoginButton\` component
- Connect to \`/api/auth/login\` endpoint
- Handle loading and error states
- Validate email format before submission
`.trim()
});
QA Bug Tracking
// Document bugs found in design
await set_multiple_annotations({
nodeId: "page-id",
annotations: foundBugs.map(bug => ({
nodeId: bug.elementId,
labelMarkdown: `**Bug ${bug.id}:** ${bug.description}\n\nSteps to reproduce:\n${bug.steps}`,
categoryId: bugCategoryId
}))
});
Troubleshooting
Annotation Not Appearing
- Verify target node type (must be COMPONENT, INSTANCE, or FRAME)
- Check node ID is correct
- Ensure node is not deleted or removed
- Verify WebSocket connection is active
Wrong Target Element
- Review matching strategy priority
- Check layer naming conventions
- Verify marker positioning
- Consider using manual
set_annotation for edge cases
Category Not Found
- Use
get_annotations with includeCategories: true to list available categories
- Verify category ID is correct
- Category may be from different Figma file
Annotation Workflow Checklist