Skip to content
admin-customize logo

Admin Customize

admin-customize

Customize the admin UI with swappable pages and layout slots. Use when replacing built-in pages (dashboard, list, edit, view), injecting content into layout slots (header, footer, sidebar, before/after), or registering per-collection overrides.

SKILL.md

Full skill instructions

Admin Customization — Swappable Pages & Layout Slots

Guide for customizing the Momentum CMS admin UI via page replacements and layout slot injection.

Arguments

  • $ARGUMENTS - What to customize: page-override, slot, or per-collection

Two Registration Methods

1. Config-level (momentum.config.ts)

Registered in the server config. The code generator emits these as loader functions in the browser-safe generated config. This is the recommended approach — single source of truth.

2. Provider-level (app.config.ts)

Registered via Angular DI providers. Useful for app-specific customizations that don't belong in the server config, or for login page slots (which render outside the admin shell).

Swappable Pages (Full Replacement)

Available Page Keys

KeyBuilt-in Page
dashboardDashboard
loginLogin
mediaMedia Library
collection-listCollection List
collection-editCollection Edit
collection-viewCollection View
global-editGlobal Edit

Config-level (momentum.config.ts)

import { defineMomentumConfig } from '@momentumcms/​core';

export default defineMomentumConfig({
	// ...
	admin: {
		basePath: '/​admin',
		components: {
			dashboard: () => import('./​app/​custom-dashboard.component').then((m) => m.CustomDashboard),
		},
	},
});

Provider-level (app.config.ts)

import { provideAdminComponent } from '@momentumcms/​admin';

export const appConfig: ApplicationConfig = {
	providers: [
		provideAdminComponent('dashboard', () =>
			import('./​custom-dashboard.component').then((m) => m.CustomDashboard),
		),
	],
};

Per-Collection Page Overrides

Override pages for a specific collection only. Other collections keep the default.

Config-level (on the collection definition)
const collections = baseCollections.map((c) => {
	if (c.slug === 'articles') {
		return {
			...c,
			admin: {
				...c.admin,
				components: {
					list: () =>
						import('./​app/​custom-articles-list.component').then(
							(m) => m.CustomArticlesListComponent,
						),
				},
			},
		};
	}
	return c;
});
Provider-level
provideAdminComponent('collections/​articles/​list', () =>
	import('./​custom-articles-list.component').then((m) => m.CustomArticlesList),
);

Resolution Chain

For collection pages, the resolver checks in order:

  1. Per-collection override (collections/​{slug}/​{type})
  2. Global override (collection-list)
  3. Built-in default

Layout Slots (Additive Injection)

Slots inject content around existing pages without replacing them. Multiple components can be registered for the same slot.

Available Slots

Shell Slots (visible on all authenticated pages)
Slot KeyPosition
shell:headerTop of main content area
shell:footerBottom of main content area
shell:nav-startAfter Dashboard link in sidebar
shell:nav-endAfter plugin routes in sidebar
Page Slots
Slot KeyPosition
dashboard:before/​afterAround dashboard content
collection-list:before/​afterAround list tables
collection-edit:before/​after/​sidebarAround edit forms
collection-view:before/​afterAround view pages
login:before/​afterAround login form

Config-level (momentum.config.ts — admin.components)

Config-level slots use camelCase keys that map to slot positions:

Config KeySlot Position
beforeNavigationshell:nav-start
afterNavigationshell:nav-end
headershell:header
footershell:footer
beforeDashboarddashboard:before
afterDashboarddashboard:after
beforeLoginlogin:before
afterLoginlogin:after
admin: {
	components: {
		beforeDashboard: () =>
			import('./​app/​announcement-banner.component').then((m) => m.AnnouncementBanner),
		footer: () =>
			import('./​app/​custom-footer.component').then((m) => m.CustomFooter),
	},
},

Config-level (per-collection slots)

// In the collection definition:
admin: {
	components: {
		beforeList: () => import('./​articles-filter.component').then((m) => m.ArticlesFilter),
		editSidebar: () => import('./​meta-panel.component').then((m) => m.MetaPanel),
		beforeView: () => import('./​status-badge.component').then((m) => m.StatusBadge),
	},
},

Per-collection config keys: beforeList, afterList, beforeEdit, afterEdit, editSidebar, beforeView, afterView.

Provider-level (app.config.ts)

import { provideAdminSlot } from '@momentumcms/​admin';

providers: [
	// Global slot (all pages)
	provideAdminSlot('shell:header', () =>
		import('./​env-banner.component').then((m) => m.EnvBanner),
	),

	// Per-collection slot
	provideAdminSlot('collection-list:before:articles', () =>
		import('./​articles-filter.component').then((m) => m.ArticlesFilter),
	),
],

Per-Collection Slot Merging

When viewing a collection page, both global and per-collection slot components render (global first, then per-collection).

Component Conventions

Custom components receive context via Angular inputs:

import { ChangeDetectionStrategy, Component, input } from '@angular/​core';
import type { CollectionConfig } from '@momentumcms/​core';

@Component({
	selector: 'app-custom-slot',
	host: { class: 'block' },
	template: `
		<div class="p-4 bg-mcms-muted rounded-lg">
			@if (collection(); as col) {
				<p>Collection: {{ col.slug }}</​p>
			}
		</​div>
	`,
	changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomSlotComponent {
	readonly collection = input<CollectionConfig>();
	readonly entityId = input<string>();
}

After Making Changes

  1. If you modified momentum.config.ts, regenerate the browser-safe config:
    nx run <app>:generate   # or npm run generate in generated apps
    
  2. The generated config at src/​generated/​momentum.config.ts will include the component loaders with correct import paths.
  3. Provider-level changes in app.config.ts take effect immediately (no generation needed).

Plugin Registration

Plugins can declare admin component overrides:

const myPlugin: MomentumPlugin = {
	name: 'my-plugin',
	adminComponents: {
		beforeDashboard: () => import('./​plugin-banner').then((m) => m.PluginBanner),
		header: () => import('./​custom-header').then((m) => m.CustomHeader),
	},
};

Exports from @momentumcms/​admin

import {
	provideAdminComponent, // Register page override via DI
	provideAdminSlot, // Register slot component via DI
} from '@momentumcms/​admin';

Related Files

  • libs/​core/​src/​lib/​config.ts — AdminComponentsConfig (global slots/​pages)
  • libs/​core/​src/​lib/​collections/​collection.types.ts — CollectionAdminComponentsConfig (per-collection)
  • libs/​admin/​src/​lib/​services/​admin-component-registry.service.ts — Page resolver registry
  • libs/​admin/​src/​lib/​services/​admin-slot-registry.service.ts — Slot registry
  • libs/​admin/​src/​lib/​services/​provide-admin-components.ts — Provider functions
  • libs/​admin/​src/​lib/​components/​admin-page-resolver/ — Route-level page resolver
  • libs/​admin/​src/​lib/​components/​admin-slot-outlet/ — Slot rendering component
  • docs/​admin/​customization.md — User-facing documentation