Angular  

Creating Reusable Components in Angular

Reusable components are the foundation of scalable Angular applications. They allow developers to write code once and use it multiple times across different features, pages, or modules. This reduces duplication, improves consistency, and makes the application easier to maintain as it grows.

Angular’s component-based architecture is designed with reusability in mind. However, creating components that are truly reusable requires planning, modular design, and adherence to certain best practices.

This article walks you through how to design, build, and manage reusable components in Angular with real examples and strategies used in production-grade applications.

1. What Makes a Component Reusable

A reusable component is:

  • Independent

  • Configurable

  • Flexible

  • Easy to integrate

  • Free from business logic

  • Able to accept inputs and emit outputs

Reusable components focus on presentation, not business logic.

Examples

  • Buttons

  • Input fields

  • Dropdowns

  • Data tables

  • Cards

  • Modals

  • Charts

  • Reusable layouts

These components become building blocks of your application.

2. Why Reusable Components Matter

Reusable components help developers:

  1. Reduce code duplication

  2. Maintain a consistent UI

  3. Increase productivity

  4. Improve maintainability

  5. Scale applications faster

  6. Reduce bugs through standard components

For large teams, reusable components create a unified UI language.

3. Setting Up a Shared Module for Reusable Components

To store reusable components, create a shared module:

ng generate module shared

Folder structure:

src/app/shared/
  components/
    button/
    card/
    modal/
  pipes/
  directives/
  shared.module.ts

Export reusable components through the shared module:

@NgModule({
  declarations: [
    ButtonComponent,
    CardComponent,
    ModalComponent
  ],
  exports: [
    ButtonComponent,
    CardComponent,
    ModalComponent
  ],
  imports: [
    CommonModule
  ]
})
export class SharedModule {}

Import SharedModule into other modules to use the components.

4. Building a Reusable Button Component

Create a reusable button:

ng generate component shared/components/button

button.component.ts:

@Component({
  selector: 'app-button',
  templateUrl: './button.component.html'
})
export class ButtonComponent {
  @Input() label!: string;
  @Input() type: 'primary' | 'secondary' = 'primary';
  @Input() disabled = false;

  @Output() clicked = new EventEmitter<void>();

  onClick() {
    if (!this.disabled) {
      this.clicked.emit();
    }
  }
}

button.component.html:

<button
  [class.primary]="type === 'primary'"
  [class.secondary]="type === 'secondary'"
  [disabled]="disabled"
  (click)="onClick()"
>
  {{ label }}
</button>

Now you can use it anywhere:

<app-button
  label="Save"
  type="primary"
  (clicked)="onSave()">
</app-button>

5. Building a Reusable Input Component

Create the input component:

ng generate component shared/components/input

input.component.ts:

@Component({
  selector: 'app-input',
  templateUrl: './input.component.html'
})
export class InputComponent {
  @Input() label!: string;
  @Input() placeholder = '';
  @Input() value = '';
  @Output() valueChange = new EventEmitter<string>();

  onInput(event: any) {
    this.valueChange.emit(event.target.value);
  }
}

input.component.html:

<label>{{ label }}</label>
<input
  [placeholder]="placeholder"
  [value]="value"
  (input)="onInput($event)"
/>

Use it anywhere:

<app-input
  label="Username"
  [(value)]="username">
</app-input>

6. Creating a Reusable Modal Component

ng generate component shared/components/modal

modal.component.ts:

@Component({
  selector: 'app-modal',
  templateUrl: './modal.component.html'
})
export class ModalComponent {
  @Input() title!: string;
  @Input() visible = false;

  @Output() closed = new EventEmitter<void>();

  close() {
    this.visible = false;
    this.closed.emit();
  }
}

modal.component.html:

<div *ngIf="visible" class="modal-overlay">
  <div class="modal-box">
    <h2>{{ title }}</h2>
    <ng-content></ng-content>
    <button (click)="close()">Close</button>
  </div>
</div>

The modal supports content projection.

Use example:

<app-modal
  title="Edit Profile"
  [visible]="showModal"
  (closed)="showModal = false">
  
  <p>Modal body content goes here</p>

</app-modal>

7. Designing Configurable Components Using Inputs

Reusable components should accept configuration properties using @Input().

Example configuration for a table:

@Input() columns: string[] = [];
@Input() rows: any[] = [];
@Input() bordered = false;
@Input() striped = false;

This allows the component to adapt to different needs.

8. Emitting Custom Events Using Outputs

Output events make components interactive.

Example event emitter:

@Output() selected = new EventEmitter<any>();

onRowClick(row: any) {
  this.selected.emit(row);
}

Usage:

<app-data-table
  [rows]="users"
  (selected)="onUserSelected($event)">
</app-data-table>

9. Using Content Projection for Reusable Layouts

Angular’s <ng-content> allows embedding custom HTML inside a reusable component.

Example: A reusable card layout.

card.component.html:

<div class="card">
  <div class="card-header">
    <ng-content select="[card-title]"></ng-content>
  </div>
  <div class="card-body">
    <ng-content select="[card-body]"></ng-content>
  </div>
</div>

Usage:

<app-card>
  <div card-title>User Details</div>
  <div card-body>Some description here</div>
</app-card>

This enables flexible, dynamic layouts.

10. Reusing Components with Dynamic Data

A good reusable component is not tied to fixed data structures.

Example for a dynamic dropdown:

@Input() options: { label: string, value: any }[] = [];

Now it can support:

  • Countries

  • Cities

  • Product categories

  • User roles

Any dataset fits this structure.

11. Building Reusable Components with Dependency Injection

Reusable components often depend on services.

Example

A reusable toast notification component using a service:

toast.service.ts:

@Injectable({
  providedIn: 'root',
})
export class ToastService {
  toast$ = new Subject<string>();

  show(message: string) {
    this.toast$.next(message);
  }
}

toast.component.ts:

@Component({
  selector: 'app-toast',
  templateUrl: './toast.component.html'
})
export class ToastComponent {
  message = '';

  constructor(private toastService: ToastService) {
    this.toastService.toast$.subscribe(msg => {
      this.message = msg;
    });
  }
}

Reusable across the whole app.

12. Reusable Component Patterns

Pattern 1: Input + Output

For small interactive components.

Pattern 2: Content Projection

For flexible layouts.

Pattern 3: ControlValueAccessor

For reusable form components.

Example: Custom input field integrated with Angular forms.

Use NG_VALUE_ACCESSOR to make custom components behave as regular form controls.

Pattern 4: Dynamic Components

Useful for dashboards, modals, widget systems.

Pattern 5: Smart vs Dumb Components

Reusable components should be dumb components, receiving data and emitting events only.

13. Reusable Form Inputs Using ControlValueAccessor

ControlValueAccessor allows custom components to be used with Angular forms.

Example

@Component({
  selector: 'app-textbox',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => TextboxComponent),
    multi: true
  }]
})
export class TextboxComponent implements ControlValueAccessor {
  value = '';

  onChange = (_: any) => {};
  onTouched = () => {};

  writeValue(val: any): void {
    this.value = val;
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
}

This enables:

<form>
  <app-textbox formControlName="name"></app-textbox>
</form>

14. Best Practices for Reusable Components

  1. Keep components small and specific

  2. Avoid business logic inside reusable components

  3. Use Input() for flexibility

  4. Use Output() for communication

  5. Use content projection for layouts

  6. Separate reusable components into a shared module

  7. Avoid coupling components with API calls

  8. Test components individually

  9. Use consistent naming conventions

  10. Document component usage for your team

Conclusion

Reusable components are essential for building scalable and maintainable Angular applications. By leveraging Angular’s component architecture, content projection, input-output bindings, and patterns such as ControlValueAccessor and smart-dumb components, developers can create flexible and powerful building blocks for UI development.

With proper structure, a shared module, and careful design decisions, your Angular application becomes more modular, robust, and easier to extend. Reusable components save time, reduce bugs, and help teams deliver high-quality applications faster.