The Mantle admin interface is a single-page React application mounted on one WordPress admin page. Routing is query-parameter based and driven entirely by a filtered array of tab objects, so adding a view never means editing a switch statement.
Rendering pipeline
From URL to rendered view
The four moving parts
-
src/constants/routes.jsstring -
Declares the core TABS object and the helper functions that resolve tabs from an ID, a path, or the query string. Applies the
mantle.navigation_tabsfilter. -
src/components/ViewWrapper.jsstring -
Reads the
viewquery parameter, resolves the tab, and renders its component inside a Card whose heading is the tab title. -
src/layouts/index.jsstring -
Loads settings and setup data, decides between DashboardLayout and SetupLayout, and shows a loading state while fetching.
-
src/modules/*/register.jsstring -
Where each module adds its tab via the filter and its panel via a SlotFill.
No fields match this filter.
Core tabs
TABS in src/constants/routes.js declares only two entries. Everything else arrives through the filter.
export const TABS = { DASHBOARD: { id: 'dashboard', path: '/dashboard', title: __( 'Dashboard', 'mantle' ), component: Dashboard, icon: DashboardIcon, order: 10, requiredCapability: 'mantle_view_dashboard', }, SETTINGS: { id: 'settings', path: '/settings', title: __( 'Settings', 'mantle' ), component: Settings, icon: SettingsIcon, order: 100, requiredCapability: 'mantle_edit_settings', },};export const getTabs = () => { const filteredTabs = Object.values( TABS ); return applyFilters( 'mantle.navigation_tabs', filteredTabs );};
-
Line 8Dashboard is order 10 — the lower bound for module tabs.
-
Line 17Settings is order 100 — the upper bound. Module tabs sit between.
-
Line 23
Object.values()converts to an array before filtering. -
Line 25So filter callbacks receive an array and must return one.
Route resolution helpers
routes.js exports several resolvers. All of them call getTabs() internally, so they see filtered tabs including third-party ones.
Exported helpers
src/constants/routes.js.-
getTabs()array -
Returns the filtered array of tab objects.
-
getTabIdFromQuery(url?)string -
Reads the
viewquery parameter and validates it against the tabs. Falls back to the Dashboard ID when absent or unrecognised. -
getTabIdFromLocation(location)string -
Resolves a tab from a React Router location’s first path segment.
-
getTabIdFromPath(path)string -
Resolves a tab ID from a path such as
/settings. -
getPathFromTabId(tabId)string -
The inverse: path for a given tab ID.
-
updateRouteQuery(tabId, replace?)any -
Pushes or replaces history state. Deletes the
viewparameter entirely for the Dashboard tab, since it is the default. -
getViewComponent(viewId)any -
Returns the component for a tab ID, or null.
-
getDefaultViewComponent()any -
Always returns the Dashboard component.
No fields match this filter.
Dashboard is special-cased in updateRouteQuery(): navigating to it removes the view parameter instead of setting view=dashboard, keeping the canonical URL clean.
Settings subtabs
The Settings view has its own nested tab system, filtered separately and gated by module state.
Settings subtab resolution
The filter receives a second argument of { settings, modules }, so a callback can decide what to add based on current state rather than relying on the automatic gating.
Capability gating is presentational
requiredCapability controls whether a tab is shown. It does not protect anything.
Data layer
The admin app reads and writes through WordPress data stores in src/stores/, and the Settings view consumes a useMantleSettings() hook that exposes settings, modules, invalidate, and saveSite. Preloaded REST responses are supplied from PHP through the mantle_admin_preload_paths filter, so the first render does not wait on network requests.
PHP filters that shape the admin app
Controller\Admin.-
mantle_admin_dataarray -
The data object handed to the JavaScript application, including REST paths.
-
mantle_admin_preload_pathsarray -
REST paths preloaded into the page so the first render is instant. Add your module’s route here.
-
mantle_admin_stringsarray -
Localised strings passed to the application.
-
mantle.admin_submenusarray -
WordPress admin submenu entries.
Abstract_Module::init()hooks each enabled module’sadd_menu_item()into this.
No fields match this filter.