How to maintain mouse capture when cursor leaves Figma plugin UI?

Issue with Mouse Tracking in Figma Plugin

I’ve been working on a custom control component that needs to track mouse movements even when the cursor goes outside the plugin window. This works perfectly in regular web browsers but seems to break in Figma’s plugin environment.

function initializeControl(element, w, h) {
    element.width = w;
    element.height = h;
    element.style.display = 'block';
    
    element.changeEvent = new Event('valueChanged');
    
    element.addEventListener('mousedown', function(event) {
        if (event.button === 0) {
            event.preventDefault();
            
            if (element.setCapture) {
                element.setCapture();
            }
            
            element.isDragging = true;
            element.startX = event.clientX;
            element.initialValue = element.value;
        }
    });
    
    element.addEventListener('losecapture', function() {
        element.isDragging = false;
    });
    
    document.addEventListener('mouseup', function(event) {
        if (event.button === 0 && element.isDragging) {
            element.isDragging = false;
        }
    }, true);
    
    (element.setCapture ? element : document).addEventListener('mousemove', function(event) {
        if (element.isDragging) {
            var deltaX = element.startX - event.clientX;
            var sensitivity = 8 * Math.pow(Math.abs(deltaX), element.acceleration);
            
            element.value = Math.min(Math.max(element.minValue, element.initialValue - deltaX * element.step * sensitivity), element.maxValue);
            element.render();
            element.dispatchEvent(element.changeEvent);
        }
    }, true);
    
    element.addEventListener('wheel', function(event) {
        event.preventDefault();
        var target = event.target;
        target.value = Math.min(Math.max(target.minValue, target.value + (event.deltaY < 0 ? 1 : -1) * target.step), target.maxValue);
        target.dispatchEvent(target.changeEvent);
        target.render();
    });
    
    element.render = function() {
        // rendering logic here
    };
    
    element.render();
}

The problem is that mouse capture gets lost as soon as the pointer moves outside the plugin window boundaries. Has anyone found a workaround for this limitation in Figma plugins?

ran into this same headache last month. figma’s sandbox environment doesnt support proper mouse capture unfortunately. ended up using pointer events instead of mouse events - works bit better but still not perfect solution.

This is a known limitation with Figma’s plugin architecture that I struggled with for weeks. The iframe sandboxing breaks standard mouse capture APIs completely. My solution was to implement a global state manager that tracks drag operations across the entire document scope. Instead of relying on setCapture, I attach listeners to the window object and maintain dragging state in a separate object that persists beyond element boundaries. The trick is to calculate relative positions from your initial mousedown coordinates and apply those deltas continuously. You also need to handle the edge case where users drag too quickly and the mouse events get missed - I added a throttled position sampling mechanism to catch those gaps. Not pretty but it works reliably in Figma’s constrained environment.

I dealt with this exact issue when building a custom slider component. The problem stems from Figma’s iframe restrictions which prevent standard capture methods from working properly. What worked for me was implementing a hybrid approach where I track the mouse position relative to the viewport and calculate movements based on the initial click coordinates. You need to store the mouse offset when dragging starts and then use document-level event listeners to maintain tracking. The key is to bind mousemove to the document rather than the element itself, and implement your own boundary detection logic. It’s not as elegant as native capture but provides consistent behavior across different plugin environments.