While recently walking a colleague through Power Automate, I was reminded of one of my long-standing frustrations: how difficult it is to copy expressions once they’ve been inserted into actions like a Compose
, Filter Array
, or For Each
. For example:
workflow()['tags']['environmentName']
item()?['logicalName']
Once fields are wrapped in certain expressions or are in certain actions, you can no longer open them in the expression popup—you or easily copy them out to reuse elsewhere. That makes it frustrating when you want to replicate logic in another flow or simply document what you’ve written. There is a copy action functionality but that copies the entire action and sometimes you just want a specific expression.
To deal with this, I used to:
title
attribute that contains the expressionBut neither of these approaches felt elegant. And since I work in the public sector, I can’t use browser extensions like a typical Power Platform developer might.
This reminded me of the old-school technique of using bookmarklets to extract information from web apps—something I used to do to grab entity IDs in Dynamics CRM. That got me thinking: why not use a bookmarklet or browser snippet to extract expressions in Power Automate?
So I built exactly that: a JavaScript snippet that works with both the classic and new editors in Power Automate.
This tool is great when you’re:
The script listens for clicks on expression tokens within the flow designer. Once you click a token, it pops open a dialog with:
It supports:
title
attribute on legacy token spansjavascript:(function(){const e=new URLSearchParams(window.location.search).get("v3"),t=null===e||"true"===e;function n(e){const t=document.getElementById("expression-dialog");t&&t.remove();const n=document.createElement("div");n.id="expression-dialog",n.style.cssText="position: fixed; top: 20%; left: 50%; transform: translateX(-50%); background: white; border: 2px solid #0078d4; padding: 20px; z-index: 99999; box-shadow: 0 4px 12px rgba(0,0,0,0.2); font-family: sans-serif; border-radius: 8px; min-width: 400px;",n.innerHTML=`<div style="margin-bottom: 10px; font-weight: bold;">Power Automate Expression</div><input type="text" id="expression-input" value="${e.replace(/"/g,""")}" style="width: 100%; padding: 8px; font-size: 14px;" readonly /><div style="margin-top: 10px; text-align: right;"><button id="turnoff-btn" style="margin-right: 10px;">Turn Off</button><button id="copy-btn" style="margin-right: 10px;">Copy</button><button id="close-btn" style="margin-right: 10px;">Close</button><button id="copy-close-btn">Copy & Close</button></div>`,document.body.appendChild(n);const o=document.getElementById("expression-input");o.focus(),o.select(),document.getElementById("copy-btn").onclick=()=>{navigator.clipboard.writeText(e).then(()=>{const e=document.getElementById("copy-btn");e.innerText="Copied!",setTimeout(()=>e.innerText="Copy",1500)})},document.getElementById("copy-close-btn").onclick=()=>{navigator.clipboard.writeText(e).then(()=>{n.remove()})},document.getElementById("close-btn").onclick=()=>n.remove(),document.getElementById("turnoff-btn").onclick=()=>{t?document.removeEventListener("click",r,!0):document.removeEventListener("click",c,!0),n.remove(),alert("Expression click handler turned off.")}}function c(e){"SPAN"===e.target.tagName&&(e.preventDefault(),e.stopPropagation(),e=e.target.parentElement?.parentElement,e&&e.classList.contains("msla-token")&&n(e.title||"(no title found)"))}function r(e){"DIV"===e.target.tagName&&(e=e.target.closest('span[data-automation-id^="flow-token"]'),e&&(e=e.getAttribute("aria-labelledby")||e.getAttribute("aria-describedby"),e=document.getElementById(e)?.querySelector("span")?.textContent?.trim())&&(event.preventDefault(),event.stopPropagation(),n(e)))}t?(alert("Click on any token in the new editor (v3) to extract the expression. You can keep clicking."),document.addEventListener("click",r,!0)):(alert("Click on any expression token (v2) to extract the expression. You can keep clicking."),document.addEventListener("click",c,!0))})();
make.powerautomate.com
)F12
to open Developer ToolsHere is the full code:
// Combined widget for both v2 and v3 Power Automate editors
(function() {
const urlParams = new URLSearchParams(window.location.search);
const v3Param = urlParams.get("v3");
const isV3 = v3Param === null || v3Param === "true";
function handleClickV2(e) {
if (e.target.tagName !== "SPAN") return;
const token = e.target.parentElement?.parentElement;
if (!token || !token.classList.contains("msla-token")) return;
e.preventDefault();
e.stopPropagation();
const expression = token.title || "(no title found)";
showDialog(expression);
}
function handleClickV3(e) {
if (e.target.tagName !== "DIV") return;
let token = e.target.closest('span[data-automation-id^="flow-token"]');
if (!token) return;
const describedById = e.target.getAttribute("aria-labelledby") || e.target.getAttribute("aria-describedby");
if (!describedById) return;
const tooltipEl = document.getElementById(describedById);
const expression = tooltipEl?.querySelector('span')?.textContent?.trim();
if (!expression) return;
e.preventDefault();
e.stopPropagation();
showDialog(expression);
}
function showDialog(expression) {
const existing = document.getElementById("expression-dialog");
if (existing) existing.remove();
const dialog = document.createElement("div");
dialog.id = "expression-dialog";
dialog.style.cssText = `
position: fixed;
top: 20%;
left: 50%;
transform: translateX(-50%);
background: white;
border: 2px solid #0078d4;
padding: 20px;
z-index: 99999;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
font-family: sans-serif;
border-radius: 8px;
min-width: 400px;
`;
dialog.innerHTML = `
<div style="margin-bottom: 10px; font-weight: bold;">Power Automate Expression</div>
<input type="text" id="expression-input" value="${expression.replace(/"/g, '"')}" style="width: 100%; padding: 8px; font-size: 14px;" readonly />
<div style="margin-top: 10px; text-align: right;">
<button id="turnoff-btn" style="margin-right: 10px;">Turn Off</button>
<button id="copy-btn" style="margin-right: 10px;">Copy</button>
<button id="close-btn" style="margin-right: 10px;">Close</button>
<button id="copy-close-btn">Copy & Close</button>
</div>
`;
document.body.appendChild(dialog);
const input = document.getElementById("expression-input");
input.focus();
input.select();
document.getElementById("copy-btn").onclick = () => {
navigator.clipboard.writeText(expression).then(() => {
const btn = document.getElementById("copy-btn");
btn.innerText = "Copied!";
setTimeout(() => btn.innerText = "Copy", 1500);
});
};
document.getElementById("copy-close-btn").onclick = () => {
navigator.clipboard.writeText(expression).then(() => {
dialog.remove();
});
};
document.getElementById("close-btn").onclick = () => dialog.remove();
document.getElementById("turnoff-btn").onclick = () => {
if (isV3) {
document.removeEventListener("click", handleClickV3, true);
} else {
document.removeEventListener("click", handleClickV2, true);
}
dialog.remove();
alert("Expression click handler turned off.");
};
}
if (isV3) {
alert("Click on any token in the new editor (v3) to extract the expression. You can keep clicking.");
document.addEventListener("click", handleClickV3, true);
} else {
alert("Click on any expression token (v2) to extract the expression. You can keep clicking.");
document.addEventListener("click", handleClickV2, true);
}
})();
Once active, simply click any expression token in the designer to see the popup.
Original Post http://www.richardawilson.com/2025/04/copy-hidden-power-automate-expressions.html