Creating a Custom Top Navigation Menu in SPFx with Application Customizer
In this technical article, we explore how to implement a custom top navigation menu in SharePoint Online using an SPFx Application Customizer. The code provided demonstrates how to inject a React component into the Top placeholder of a modern SharePoint page using the SPFx extension model.
This approach is useful for organizations that want to extend SharePoint’s out-of-the-box navigation without using classic methods or altering master pages.
Technical Overview
1. Imports and Dependencies
import * as ReactDOM from 'react-dom';
import * as React from 'react';
import { Log } from '@microsoft/sp-core-library';
import {
BaseApplicationCustomizer,
PlaceholderContent,
PlaceholderName
} from '@microsoft/sp-application-base';
import NavigationMenu from './NavigationMenu';
import * as strings from 'TopMenuApplicationCustomizerStrings';
- React & ReactDOM: Used to render the navigation menu as a React component.
- BaseApplicationCustomizer: The base class for creating SharePoint extensions that customize placeholders.
- PlaceholderContent & PlaceholderName: Used to manage rendering in the predefined
TopandBottomregions of the page. - NavigationMenu: The React component that will be injected into the placeholder.
- strings: Localization strings for the extension (can be customized per language).
2. Extension Properties Interface
export interface ITopMenuApplicationCustomizerProperties {
// This is an example; replace with your own property
}
- This is a placeholder for passing configuration via
ClientSideComponentProperties. It is not used in this example but can be extended to support dynamic configuration.
3. Class Declaration and Initialization
export default class TopMenuApplicationCustomizer
extends BaseApplicationCustomizer<ITopMenuApplicationCustomizerProperties> {
The class TopMenuApplicationCustomizer extends BaseApplicationCustomizer, meaning it will be executed when SharePoint initializes the client-side application.
private _topPlaceholder: PlaceholderContent | undefined;
This private variable is used to ensure the navigation is only rendered once.
public async onInit(): Promise<void> {
Log.info(LOG_SOURCE, `Initialized ${strings.Title}`);
this.context.placeholderProvider.changedEvent.add(this, this._renderPlaceHolders);
return Promise.resolve();
}
- onInit(): Subscribes to the
changedEventof theplaceholderProvider, which triggers when placeholders become available on the page. - It then calls
_renderPlaceHolders()to render the top navigation.
4. Rendering the Placeholder
private _renderPlaceHolders(): void {
This method checks for available placeholders and renders the navigation component if the Top placeholder is available.
this._topPlaceholder = this.context.placeholderProvider.tryCreateContent(
PlaceholderName.Top,
{ onDispose: this._onDispose }
);
- tryCreateContent() attempts to create a content region in the top placeholder.
- If successful, it renders the
NavigationMenuReact component:
const reactElt: React.ReactElement = React.createElement(
NavigationMenu,
{
type: "Default",
ListTitle: "TopMenu",
AppContext: this.context,
siteUrl: "https://sharepoint.com/sites/site"
}
);
ReactDOM.render(reactElt, this._topPlaceholder.domElement);
This code dynamically injects the navigation menu using values such as the list title and site URL as props.
5. Cleanup Logic
private _onDispose(): void {
console.log('[HelloWorldApplicationCustomizer._onDispose] Disposed custom top and bottom placeholders.');
}
This function is called when the placeholder content is removed, ensuring memory is properly managed.
Use Cases
- Enterprise-wide top navigation menus.
- Context-aware navigation based on site or user role.
- Integration with SharePoint lists or external APIs to dynamically build menus.
Summary Table
| Section | Purpose |
|---|---|
BaseApplicationCustomizer | Base class for SPFx Application Customizer extensions |
placeholderProvider | Provides hooks into the page’s placeholder locations |
NavigationMenu | React component injected into the top placeholder |
tryCreateContent() | Safely attempts to use a placeholder (like Top or Bottom) |
React.createElement() | Creates the navigation component to render |
ReactDOM.render() | Injects the component into the page DOM |
onDispose() | Cleanup logic to avoid memory leaks |
onInit() | Entry point that sets up placeholder change detection |
