Skip to main content
These coding standards ensure consistency throughout the Angular Material codebase and help maintain high code quality.
The Google TypeScript Style Guide is the basis for our coding style.

General Principles

Write Useful Comments

Explain why, not just what

Prefer Focused Components

Small, granular components over complex, configurable ones

Small Modules

Keep files under 400 lines when possible

Less is More

Avoid adding features that don’t offer high value

Comments

Write Useful Comments

Comments that just describe what code does:
// Set default tabindex.
if (!$attrs['tabindex']) {
  $element.attr('tabindex', '-1');
}
This comment doesn’t add value - it just repeats what the code does.

Comment Styles

/**
 * JsDoc-style comments for descriptions
 * Used on classes, methods, properties
 */
class MyComponent {
  /** The current theme color. */
  color: string;

  // Regular comments for explanations
  // Use for background info and context
}

Component Design

Prefer Focused Components

<mat-button>Basic button</mat-button>
<mat-button class="mat-fab">FAB</mat-button>
<mat-button class="mat-icon-button">icon</mat-button>
❌ Using classes to drastically change behavior

Module Organization

1

Keep modules focused

Single responsibility principle - one module, one purpose
2

Ideal file size

Target 200-300 lines per file
3

Refactor threshold

Start refactoring around 400 lines (excluding long constants/comments)

Code Style

Column Limit

All code and docs must be 100 columns or fewer.
This applies to:
  • TypeScript
  • SCSS
  • HTML
  • Bash scripts
  • Markdown files

API Design

Avoid Boolean Arguments

function getTargetElement(createIfNotFound = false) {
  // "Do something extra" parameter
}
❌ Boolean arguments that mean “do something extra” are unclear.

TypeScript

Typing

// ❌ Bad
function process(data: any) {
  // ...
}

// ✅ Good
function process<T>(data: T) {
  // ...
}

// ✅ Also good
function process(data: unknown) {
  if (typeof data === 'string') {
    // Type narrowing
  }
}
export class MyComponent {
  // ✅ Explicit return type
  getValue(): string {
    return this._value;
  }

  // ✅ Explicit parameter types
  setValue(value: string): void {
    this._value = value;
  }

  private _value: string = '';
}
Documentation tooling requires explicit types for public APIs.

Fluent APIs

class ConfigBuilder {
  // Use 'this' return type for chaining
  withName(name: string): this {
    this.config.name = name;
    return this;
  }

  withColor(color: string): this {
    this.config.color = color;
    return this;
  }
}

// Usage:
const config = new ConfigBuilder()
  .withName('my-component')
  .withColor('primary');

Access Modifiers

public
default
Omit the public keyword - it’s the default
private
_prefixed
Use private with underscore prefix for truly private members
protected
no prefix
Use protected when appropriate, no prefix
_internal
underscore only
Prefix library-internal properties with underscore without private keyword
export class MyComponent {
  // ✅ Public - no keyword
  value: string;

  // ✅ Private - keyword + prefix
  private _state: State;

  // ✅ Protected - keyword only
  protected config: Config;

  // ✅ Internal - prefix only (used in templates)
  _elementRef: ElementRef;
}
Use @docs-private JSDoc annotation to hide symbols from public API docs.

Getters and Setters

1

Use for @Input properties

@Input()
get disabled(): boolean {
  return this._disabled;
}
set disabled(value: BooleanInput) {
  this._disabled = coerceBooleanProperty(value);
}
private _disabled = false;
2

Keep them simple

If logic takes more than 3 lines, extract to a method.
3

Getter before setter

Always place getter immediately before its setter.
4

Decorate getters only

@Input()  // ✅ On getter
get value(): string {
  return this._value;
}
set value(v: string) {  // No decorator
  this._value = v;
}
5

Prefer readonly properties

// ✅ Prefer this
readonly active: boolean;

// ❌ Over this
get active(): boolean {
  return this._active;
}

JsDoc Comments

/** The label position relative to the checkbox. Defaults to 'after'. */
@Input() labelPosition: 'before' | 'after' = 'after';

Naming

  • Write out words instead of abbreviations
  • Prefer exact names: labelPosition over align
  • Use is and has prefixes for booleans (except @Input properties)
  • Don’t suffix observables with $
// ❌ Bad - describes usage
class RadioService { }

// ✅ Good - describes purpose
class UniqueSelectionDispatcher { }
  • Name based on responsibility
  • Avoid “Service” suffix
  • Think of it as a job title
  • Prefix directives: Mat for Material, Cdk for CDK
// ❌ Bad - describes when it's called
handleClick() {
  // ...
}

// ✅ Good - describes what it does
activateRipple() {
  // ...
}
Capture the action performed, not when it’s called.
Components:
  • Lowercase with hyphens: mat-button
  • Use element selectors (exceptions for native elements)
Directives:
  • camelCase: matTooltip
  • Exceptions for components with empty templates

Patterns to Avoid

Don’t Use Inheritance for Behavior

// ❌ Limits composition
class MyButton extends BaseButton {
  // Can only inherit from one class
}

Use Input Transforms

import { Input, booleanAttribute, numberAttribute } from '@angular/core';

@Input({ transform: booleanAttribute }) disabled: boolean = false;
@Input({ transform: numberAttribute }) tabIndex: number = 0;
This allows:
<component disabled></component>
<component tabIndex="5"></component>

CSS / SCSS

Be Cautious with Flexbox

  • Flex baseline calculation differs from other display values
  • Difficult to align flex elements with standard elements
  • Never use flex on elements with projected content
  • Component outermost elements should be block or inline-block

Specificity

.mat-calendar {
  display: block;

  .mat-month {
    display: inline-block;

    .mat-date.mat-selected {
      font-weight: bold;
    }
  }
}
❌ Nested selectors increase specificity

Best Practices

1

Never set margin on host

Let users control component spacing.
2

Style host when possible

// ✅ Prefer
the-host-element {
  color: red;
}

// ❌ Over
the-host-element {
  .some-child-element {
    color: red;
  }
}
3

Support high-contrast mode

@include cdk-high-contrast(active, off) {
  .mat-button {
    border: 1px solid #fff !important;
  }
}
4

Document CSS classes

// The calendar icon button used to open the calendar pane.
.mat-datepicker-button { ... }

// Floating pane that contains the calendar.
.mat-datepicker-calendar-pane { ... }
5

Use classes over tag names

// ✅ Do
.mat-mdc-slider { ... }

// ❌ Don't
mdc-slider { ... }

Angular Specific

Host Bindings

@Component({
  host: {
    '(click)': 'onClick($event)',
    '[class.active]': 'isActive',
  }
})
✅ Prevents type preservation issues

Expose Native Inputs

<!-- ✅ Do -->
<your-component>
  <input>
</your-component>

<!-- ❌ Don't -->
<your-component></your-component>
Let users interact directly with native inputs via ng-content.

Resources

TypeScript Style Guide

Google’s official TypeScript guide

Contributing

How to contribute to Angular Material

Dev Environment

Set up your development environment

Component Harnesses

Testing guidelines

Build docs developers (and LLMs) love