Extending Desk with Shell Context Hooks
Building complex enterprise user interfaces requires a delicate balance: core application shells must remain stable and secure, while domain plugins and custom UI extensions must be able to hook into form controllers, state lifecycle, and global navigation effortlessly.
In Framework M Desk (@framework-m/desk), we introduced the official React Shell Context API and consolidated Micro-Frontend (MFE) Plugin Contracts.
1. The Desk Shell Context API (@framework-m/desk)
Instead of relying on fragile global window objects or monkey-patching DOM elements, custom components in Framework M Desk interact with the parent shell via strongly typed React hooks:
import React from "react";
import { useFormController, useCall, useShell } from "@framework-m/desk";
export function CustomApprovalWidget() {
const { frm } = useFormController();
const { call, loading } = useCall();
const shell = useShell();
const handleApprove = async () => {
// Invoke backend method cleanly
const response = await call("my_app.api.approve_document", { docname: frm.doc?.name });
if (response?.success) {
frm.setDocValue("workflow_state", "Approved");
await frm.save();
}
};
return (
<button onClick={handleApprove} disabled={loading || frm.isSaving}>
{loading || frm.isSaving ? "Approving..." : "Approve Document"}
</button>
);
}
Key Context Hooks Reference
useFormController(): Provides form controllerfrm(frm.doc,frm.setDocValue,frm.save,frm.isLocked), schema metadata, and record ID.useCall(): Executes backend RPC endpoints with automatic CSRF token management and error handling.useShell(): Grants access to global shell notifications, focus mode, and drawer state.useComponentOverride(): Allows custom plugins to override standard DocType form views or list views dynamically.
2. Micro-Frontend (MFE) Plugin Registration
Framework M allows domain applications to package frontend extensions as standalone Vite Micro-Frontend (MFE) bundles.
The PluginRegistry manages dynamic plugin discovery, lifecycle diagnostics, and route registration:
import { PluginRegistry } from "@framework-m/plugin-sdk";
const registry = PluginRegistry.getInstance();
await registry.register({
name: "personnel-management",
version: "1.2.0",
ownedDocTypes: ["Employee", "Department", "Attendance"],
discoveryUnit: { apiUrl: "/api/hr" },
// Navigation Menus (Sidebar & Navigation Rail)
manifests: [
{
app_id: "hr",
label: "Human Resources",
resources: [
{
name: "hr.org_chart",
label: "Org Chart",
route: "/desk/hr/org-chart",
},
],
},
],
// React Router Page Mounts
routes: [
{
path: "/desk/hr/org-chart",
element: () => import("./pages/OrgChart"),
},
],
});
Safety & Lifecycle Diagnostics
- Owned DocType Boundaries: Plugins explicitly declare which DocTypes they own, preventing conflicting route bindings.
- Lifecycle Diagnostics: The Desk shell verifies version compatibility, missing context providers, and unhandled bundle exceptions gracefully, isolating failures so one faulty plugin never crashes the entire shell.
Reference & Guides
Explore the consolidated reference documentation:

