---
name: angular-components-shared-ui-library-contract
description: "Audits and designs Angular shared UI/component libraries in Nx monorepos to keep reusable UI contracts free of feature, domain, data-access, store, router, and backend concerns."
license: MIT
metadata:
ngautopilot-id: "angular.components.shared-ui-library-contract"
ngautopilot-source: "skills/angular/components/shared-ui-library-contract/SKILL.md"
ngautopilot-version: "0.6.0"
---
# Shared UI Library Contract
## Purpose
Use this skill to audit or design Angular shared UI and component libraries in Nx monorepos.
A shared UI library must behave as a reusable contract, not as a shortcut for app-specific logic. It should expose presentational components, stable APIs, projected content slots, styling conventions, and low-level composition primitives without leaking feature, domain, data-access, store, router, or backend concerns.
The core rule is simple:
```txt
Shared UI receives data and emits events.
Feature libraries decide what the data means.
Data-access libraries fetch and persist data.
Domain libraries own business concepts.
```
## When to Use
Use this skill when:
- creating a reusable Angular component library
- reviewing a `shared/ui`, `ui-kit`, `design-system`, or `component-library`
- auditing Nx dependency graph leaks
- a shared component injects services, stores, facades, routers, API clients, or domain-specific dependencies
- a component has too many configuration inputs
- a reusable component imports models from a feature or domain library
- a shared modal, overlay, dialog, dropdown, tooltip, drag-drop, or focus trap reimplements behavior already available in Angular CDK
- refactors across multiple apps are becoming expensive because "shared" code is not really shared
## Do
Keep shared UI dumb and explicit:
```ts
@Component({
selector: 'lib-table',
template: `
{{ column.label }}
{{ column.value(row) }}
`,
})
export class TableComponent {
@Input({ required: true }) rows: readonly T[] = [];
@Input({ required: true }) columns: readonly TableColumn[] = [];
@Output() rowSelected = new EventEmitter();
}
```
Prefer content projection over endless configuration:
```ts
@Component({
selector: 'lib-card',
template: `
`,
})
export class CardComponent {}
```
Consumer-owned content stays in the app or feature library:
```html
Product detail
```
Enforce module boundaries in Nx:
```json
{
"name": "shared-ui",
"projectType": "library",
"tags": ["type:ui", "domain:shared"]
}
```
```js
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
allow: [],
depConstraints: [
{
sourceTag: 'type:ui',
onlyDependOnLibsWithTags: ['type:ui', 'type:util', 'domain:shared']
},
{
sourceTag: 'type:feature',
onlyDependOnLibsWithTags: [
'type:ui',
'type:domain',
'type:data-access',
'type:util'
]
},
{
sourceTag: 'type:data-access',
onlyDependOnLibsWithTags: ['type:domain', 'type:util']
}
]
}
]
}
}
```
Build on Angular CDK instead of rebuilding behavior:
```txt
CDK owns behavior.
Design system owns look.
Feature libraries own content.
```
Example Nx project tags:
```json
{
"name": "shared-ui",
"projectType": "library",
"root": "libs/shared/ui",
"sourceRoot": "libs/shared/ui/src",
"tags": ["type:ui", "domain:shared"]
}
```
Example ESLint boundaries:
```js
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
allow: [],
depConstraints: [
{
sourceTag: 'type:ui',
onlyDependOnLibsWithTags: ['type:ui', 'type:util', 'domain:shared']
},
{
sourceTag: 'type:feature',
onlyDependOnLibsWithTags: ['type:ui', 'type:domain', 'type:data-access', 'type:util']
}
]
}
]
}
}
```
## Do Not
Avoid smart logic in shared UI:
```ts
export class ProductTableComponent {
private readonly productsService = inject(ProductsService);
private readonly store = inject(Store);
readonly products$ = this.store.select(selectProducts);
}
```
Avoid shared components that navigate, fetch data, or own domain rules:
```ts
this.router.navigate(['/products', product.id]);
this.http.get('/api/products');
```
Avoid over-configured APIs that become a second framework:
```ts
@Input() config!: {
title: string;
subtitle: string;
actions: Array<{
label: string;
icon: string;
visibleWhen: (row: ProductDto) => boolean;
execute: (row: ProductDto) => void;
}>;
};
```
Avoid reimplementing low-level interaction behavior that Angular CDK already provides for overlays, dialogs, focus management, drag and drop, portals, menus, scrolling, and accessibility primitives.
## Review Checklist
- [ ] The library is actually shared UI and not mixed with feature, domain, data-access, router, or store concerns.
- [ ] Shared UI components do not inject feature-specific services.
- [ ] Shared UI components do not make API calls.
- [ ] Shared UI components do not own app-specific state or navigation.
- [ ] Public APIs are small, stable, and typed.
- [ ] Content-heavy regions use `ng-content` instead of excessive inputs.
- [ ] Nx tags exist for affected projects.
- [ ] `@nx/enforce-module-boundaries` is configured and enforced.
- [ ] Angular CDK is reused where complex behavior already exists.
- [ ] Public API exports only reusable contracts.
## Expected Output
When this skill is used, the agent should:
1. Classify the library as shared UI, feature, data-access, domain, util, or mixed.
2. Inspect imports, injected dependencies, and public API shape.
3. Flag any dependency or boundary leakage.
4. Recommend projection, typed inputs, or CDK primitives where appropriate.
5. Produce a refactor plan with severity and validation steps.