Creating a Component
This guide covers the end-to-end process for adding a new publishable component to Synapse UI.
Prerequisites
- Nx workspace scaffolded (Phase 0 complete)
- Familiarity with Component Strategy
Step 1: Generate the Library
nx g @nx/angular:library my-component \
--directory=libs/ui \
--publishable \
--importPath=@synapse-ui/my-component \
--standalone \
--tags=scope:ui,type:lib
This creates:
libs/ui/my-component/
├── src/
│ ├── index.ts
│ └── lib/
├── ng-package.json
├── project.json
└── README.md
Step 2: Implement the Component
Follow the component strategy conventions:
// my-component.component.ts
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
@Component({
selector: 'synapse-my-component',
standalone: true,
templateUrl: './my-component.component.html',
styleUrl: './my-component.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MyComponentComponent {
readonly label = input<string>('');
readonly clicked = output<void>();
}
Checklist
- Selector prefixed with
synapse- -
ChangeDetectionStrategy.OnPush - Signal-based
input()andoutput() - Styles use
--synapse-*tokens only - Public API exported from
src/index.ts
Step 3: Add Storybook Stories
// my-component.stories.ts
import type { Meta, StoryObj } from '@storybook/angular';
import { MyComponentComponent } from './my-component.component';
const meta: Meta<MyComponentComponent> = {
title: 'UI/MyComponent',
component: MyComponentComponent,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<MyComponentComponent>;
export const Default: Story = {
args: { label: 'Hello' },
};
Required stories: Default, Disabled (if applicable), Theme variants.
Step 4: Write Tests
// my-component.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyComponentComponent } from './my-component.component';
describe('MyComponentComponent', () => {
let fixture: ComponentFixture<MyComponentComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyComponentComponent],
}).compileComponents();
fixture = TestBed.createComponent(MyComponentComponent);
fixture.detectChanges();
});
it('should create', () => {
expect(fixture.componentInstance).toBeTruthy();
});
});
Step 5: Document the Component
- Create
docs/components/my-component.mdfollowing the existing template. - Add a link in docs/README.md if it's a public component.
- Update the component catalog table in README.md.
Step 6: Verify Build
nx lint my-component
nx test my-component
nx build my-component
Step 7: Integrate in Sandbox
Add the component to apps/sandbox to validate real-world usage:
import { MyComponentComponent } from '@synapse-ui/my-component';
Module Boundary Rules
| Your component | Can import |
|---|---|
libs/ui/* | @synapse-ui/theme, @synapse-ui/core |
libs/ui/* | Other @synapse-ui/* UI libs (avoid circular deps) |
libs/core | @synapse-ui/theme only |
libs/theme | Nothing from UI or core |
Enforced via @nx/enforce-module-boundaries ESLint rule.