You want a button on a record's form — "Quick Actions," "Send for Approval," whatever the scenario — that pops open a purpose-built screen instead of just editing a field. That screen is a Custom Page, and getting a button to open one takes two small pieces: the page itself, and a command wired to open it. This post covers both, plus the JavaScript version when you need real control over how it opens.
- A Custom Page is a canvas-app-style screen embedded inside a model-driven app
- The modern command bar can open one with zero code, using a Power Fx Navigate action
- For full control (size, position, passing data), use Xrm.Navigation.navigateTo() instead
- A custom page opens as a dialog or side pane — it doesn't navigate the user away from the record
Custom Page vs. Power Pages — don't mix these up
Same word, different products. A Custom Page is a canvas-app-style screen that lives inside a model-driven app, for your internal users. Power Pages (covered in its own course on this site) is a full external-facing website for people outside your organization. This post is about the former — a screen your own team sees, opened from a button on a form.
Step 1 — Build the custom page
- In the model-driven app's designer, go to Pages → New page → Custom page.
- Design it like any canvas app — controls, data sources, formulas.
- Save and publish it.
- Note its unique name (visible in the page's properties, something like
cr123_mycustompage_e6a2b) — the button needs this exact value.
Step 2a — The low-code way (modern command bar)
If you don't need to pass data or control the dialog size, this is the fastest path:
- Open the form (or table) in the app designer, go to the Command bar designer.
- Add a new button, set its label and icon.
- Set its action to a Power Fx formula:
Navigate(YourCustomPageName) - Save and publish.
That's genuinely it for the simple case — no code, no ribbon XML.
Step 2b — The JavaScript way (when you need control)
For control over dialog size, position, or passing the current record's ID into the page, add a command with a Run JavaScript action instead, pointing at a function like this:
function openQuickNotePage(primaryControl) {
var pageInput = {
pageType: "custom",
name: "cr123_mycustompage_e6a2b",
entityName: primaryControl.data.entity.getEntityName(),
recordId: primaryControl.data.entity.getId()
};
var navigationOptions = {
target: 2,
width: { value: 60, unit: "%" },
height: { value: 70, unit: "%" },
position: 1
};
Xrm.Navigation.navigateTo(pageInput, navigationOptions).then(
function success() {
// page was closed
},
function error(err) {
console.log("Could not open custom page: " + err.message);
}
);
}
Two things worth calling out in that code:
entityNameandrecordIdpass the current record into the page — inside the custom page, read them withParam("recordId")andParam("entityName")in a Power Fx formula, so the page can look up or react to the record it was opened from.target: 2opens it as a centered dialog;target: 1would open it inline instead, replacing the current view rather than floating over it.
A note on the unique name
The name value has to match the custom page's unique name exactly. If the page gets renamed or recreated, that name can change — if the button suddenly stops opening anything, this is the first thing to check.