Commands, Buttons & Custom Page Navigation
Customise the model-driven app command bar with Power Fx and JavaScript. Add custom buttons, configure visibility rules, and navigate users to custom pages.
Making apps your own
Think of the command bar as a restaurant menu board.
The standard menu shows common items. But you want to add a “Daily Special” button that only appears on Fridays, a “VIP Order” button that only managers can see, and a “Feedback” button that opens a custom form. Command bar customisation lets you do exactly this — add, modify, and conditionally show buttons in model-driven apps.
Custom pages are like hidden rooms in the restaurant. Navigation sends users to canvas-app-like experiences embedded within the model-driven app — perfect for wizards, dashboards, or specialised forms that go beyond standard form layouts.
Two ways to customise commands
| Feature | Power Fx Commanding (Modern) | JavaScript Commanding (Classic) |
|---|---|---|
| Language | Power Fx formulas | JavaScript + XML ribbon definitions |
| Editor | Modern command designer (visual) | Ribbon Workbench or raw XML |
| Visibility rules | Power Fx Visible property | EnableRules/DisplayRules in XML |
| Actions | Power Fx OnSelect (Navigate, Patch, etc.) | JavaScript function calls |
| Context access | Self.Selected.Item, Self.Selected.AllItems | PrimaryControl (formContext), SelectedControl |
| Custom pages | Navigate(CustomPage) directly | Xrm.Navigation.navigateTo() |
| Complexity limit | Simple to medium actions | Any complexity — full JavaScript |
| Recommended for | New development, simple commands | Complex logic, legacy systems, advanced scenarios |
Power Fx commanding example
A “Generate Report” button that only appears when the record status is Active:
// Visible property (controls when the button shows)
Self.Selected.Item.Status = "Active"
// OnSelect (what happens when clicked)
Navigate(ReportGeneratorPage, {CaseId: Self.Selected.Item.CaseId})
JavaScript commanding example
The same button using JavaScript:
// Visibility rule — registered in ribbon XML
function isRecordActive(primaryControl) {
var formContext = primaryControl;
var status = formContext.getAttribute("statuscode").getValue();
return status === 1; // Active
}
// Action — registered in ribbon XML
function generateReport(primaryControl) {
var formContext = primaryControl;
var caseId = formContext.data.entity.getId();
Xrm.Navigation.navigateTo(
{ pageType: "custom", name: "new_reportgenerator", recordId: caseId },
{ target: 2, width: 800, height: 600, position: 1 } // Dialog
);
}
Navigating to custom pages
Custom pages are canvas-app-like experiences embedded in model-driven apps. They are ideal for:
- Wizards — multi-step forms that guide users through a process
- Dashboards — visual summaries with charts and KPIs
- Complex forms — layouts that go beyond what standard model-driven forms offer
Navigation options
| Method | Opens As | Use Case |
|---|---|---|
navigateTo with target: 1 | Full page (replaces current view) | Main workflow pages |
navigateTo with target: 2 | Dialog (centered popup) | Quick actions, wizards, confirmations |
navigateTo with target: 2, position: 2 | Side panel | Reference information while working on a form |
Power Fx Navigate() | Inline (within model-driven nav) | From Power Fx commanding |
Scenario: Kai builds a shipment wizard
Kai creates a “New Shipment” command button on the Shipment table’s main grid. When clicked, it opens a custom page as a dialog — a 3-step wizard:
Step 1: Select destination (dropdown populated from Dataverse) Step 2: Add package details (dimensions, weight, special handling) Step 3: Review and confirm
The wizard is built as a canvas custom page with navigation between screens. When the user confirms, the page creates the Shipment record via Patch() and closes the dialog.
Why a custom page? Standard model-driven forms do not support multi-step wizards. Custom pages provide the canvas app flexibility within the model-driven app context.
A developer needs to add a 'Quick View' button to a model-driven app that opens a summary panel on the right side of the screen without navigating away from the current form. Which approach is correct?
🎬 Video coming soon
Next up: PCF Components — building your first code component with TypeScript, understanding the lifecycle, and configuring the manifest.