Create a separate React custom navigation widget for the multilevel vertical menu; the managed portal’s documented Site menu editor handles item names, URLs, ordering, and removal, but does not expose a documented control for adding child indicators such as chevrons.^1^
In the developer portal administrative interface:
- Select Custom widgets → Add new custom widget.
- Enter a name such as
Vertical navigation.
- Select React, then Save.
- In a local terminal, download the generated scaffold and start it:
npx @azure/api-management-custom-widgets-scaffolder
cd <generated-widget-folder>
npm install
npm start
The scaffold separates runtime code under src/app from editor code under src/editor.^2^
2. Implement recursive menu levels and chevrons
In the generated runtime component under src/app, use a recursive menu model. This creates a chevron only when an item has children:
type Item = { label: string; href?: string; children?: Item[] };
const items: Item[] = [
{ label: "Getting started", href: "/getting-started" },
{ label: "APIs", children: [
{ label: "Payments", href: "/apis/payments" },
{ label: "Orders", children: [
{ label: "Create order", href: "/apis/orders/create" }
]}
]}
];
const MenuItem = ({ item }: { item: Item }) => item.children ? (
<li><details><summary>{item.label}</summary>
<ul>{item.children.map((x, i) => <MenuItem item={x} key={i} />)}</ul>
</details></li>
) : <li><a href={item.href}>{item.label}</a></li>;
export default () => <nav aria-label="API navigation">
<ul className="tree">{items.map((x, i) => <MenuItem item={x} key={i} />)}</ul>
</nav>;
Add a stylesheet beside the runtime component and import it from that component using the filename you chose, for example import "./menu.css";:
.tree, .tree ul { list-style: none; margin: 0; padding: 0; }
.tree ul { padding-left: 1.25rem; }
.tree a, .tree summary {
display: block; padding: .55rem .75rem;
color: inherit; text-decoration: none; cursor: pointer;
}
.tree summary { list-style: none; position: relative; padding-right: 2rem; }
.tree summary::-webkit-details-marker { display: none; }
.tree summary::after {
content: "›"; position: absolute; right: .75rem;
transition: transform .15s ease;
}
.tree details[open] > summary::after { transform: rotate(90deg); }
.tree a:hover, .tree summary:hover { background: rgba(0, 0, 0, .08); }
Using native <details> and <summary> provides keyboard-operable expansion without custom JavaScript. Move items into the widget’s custom properties if portal administrators need to edit the menu without rebuilding; the scaffold supports values configured through the widget editor.^2^
3. Deploy and publish
- Configure
resourceId and managementApiEndpoint in the generated deploy.js.
- Deploy:
npm run deploy
- Sign in when prompted. Deployment requires a member account—not a guest or federated account—from the Microsoft Entra tenant associated with the subscription, plus permission to access the portal administrative interface.
- Add the deployed widget to the required portal page.
- Save, then republish the developer portal. Updated widget code does not reach production until the portal is republished.^2^
This creates an independent navigation widget; it does not inject chevrons into or restyle the built-in Site menu. Avoid targeting generated classes or DOM selectors in the managed portal because those implementation details are not documented as stable. Portal visuals can also differ between older components and the newer Fluent UI-based design, although the documented custom-widget extensibility model remains the appropriate boundary.^3^
If the requirement is specifically to alter the built-in navigation component globally, the documented alternatives are self-hosting the portal and modifying its codebase, or using the WordPress developer-portal plugin for broader stylesheet and menu control. Self-hosting requires ownership of deployment, patching, hosting, and upgrades.^3^
References
- Tutorial: Access and customize the developer portal
- Extend the developer portal with custom widgets
- Overview of the developer portal