| name | component |
| description | Generate an Angular component with signals, OnPush, and host-based styling following Momentum CMS conventions. Use when creating new UI components in any library. |
| argument-hint | <library> <component-name> |
Generate Angular Component
Create an Angular component following Momentum CMS conventions.
Arguments
- First argument: Library path (e.g., "admin", "ui", "admin/components")
- Second argument: Component name in kebab-case (e.g., "data-table", "field-renderer")
File Naming Convention
Momentum CMS uses short file names without the .component infix. This is a deliberate project convention — do NOT follow the Angular CLI default of .component.ts.
| File | Name it | NOT |
|---|
| Component | <name>.ts | <name>.component.ts |
| Template | <name>.html | <name>.component.html |
| Tests | <name>.spec.ts | <name>.component.spec.ts |
For example, a component called data-table produces:
data-table.ts
data-table.html (only if template is complex)
data-table.spec.ts
Steps
-
Create component files in libs/<library>/src/lib/<component>/:
<component>.ts — the component class (NOT <component>.component.ts)
<component>.html — template, only if complex (NOT <component>.component.html)
<component>.spec.ts — tests (NOT <component>.component.spec.ts)
-
Use this template for the component:
import { ChangeDetectionStrategy, Component, computed, input, output, signal, inject } from '@angular/core';
@Component({
selector: 'mcms-<component-name>',
host: { class: 'block' },
template: `
<!-- Template here -->
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class <PascalName>Component {
readonly data = input.required<DataType>();
readonly disabled = input(false);
readonly valueChange = output<ValueType>();
private readonly _loading = signal(false);
readonly loading = this._loading.asReadonly();
readonly hasData = computed(() => !!this.data());
private readonly http = inject(HttpClient);
}
- Export from library's
index.ts:
export { <PascalName>Component } from './lib/<component>/<component>';
Note: the import path ends with /<component> (the short filename), NOT /<component>.component.
Key Conventions
- NO
standalone: true — default in Angular 21, redundant (ESLint enforced)
- NO
any types — use proper interfaces (ESLint enforced)
- NO
CommonModule import — unnecessary in Angular 21
- NO
.component.ts suffix — use plain .ts (project convention, see File Naming above)
- Always set
changeDetection: ChangeDetectionStrategy.OnPush — never use Default
- Use
input() and input.required() for inputs — never use @Input() decorator
- Use
output() for outputs — never use @Output() decorator
- Use
signal() for internal state, computed() for derived values
- Use
inject() for dependency injection — never use constructor injection
- Use
@for/@if/@switch control flow — never use *ngFor/*ngIf/[ngSwitch]
- Prefix selectors with
mcms-
UI Component Patterns
No Wrapping Divs
Angular components create a host element. Style the host directly — do NOT add wrapper divs:
@Component({
selector: 'mcms-button',
host: { class: 'inline-flex items-center gap-2' },
template: `<ng-content />`,
})
NOT:
template: `<div class="inline-flex items-center gap-2"><ng-content /></div>`,
Class Configuration
Accept a class input for Tailwind customization via hostClasses() computed:
@Component({
selector: 'button[mcms-button]',
host: { '[class]': 'hostClasses()' },
template: `<ng-content />`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class McmsButtonComponent {
readonly variant = input<'primary' | 'secondary'>('primary');
readonly class = input('');
readonly hostClasses = computed(
() => `${baseClasses} ${variantClasses[this.variant()]} ${this.class()}`,
);
}
Theme Service
Use McmsThemeService for dark mode support:
import { McmsThemeService } from '@momentumcms/admin';
@Component({...})
export class MyComponent {
private readonly theme = inject(McmsThemeService);
toggleDarkMode(): void {
this.theme.toggleTheme();
}
readonly isDark = this.theme.isDark;
readonly currentTheme = this.theme.theme;
}
App Tailwind Setup
Apps using the admin library must:
- tailwind.config.js — Use the admin preset and include library sources:
const adminPreset = require('../../libs/admin/tailwind.preset');
module.exports = {
presets: [adminPreset],
content: [
join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'),
join(__dirname, '../../libs/admin/src/**/*.{ts,html}'),
...createGlobPatternsForDependencies(__dirname),
],
};
- styles.css — Include CSS variables inline (Angular's esbuild can't resolve library CSS imports):
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--mcms-background: 0 0% 100%;
--mcms-foreground: 222 47% 11%;
}
.dark {
}
}