Angular 91 🅰️ Angular CDK
The Angular CDK — Component Dev Kit — is a set of behavior primitives for building UI components. It is the foundation that Angular Material is built on, but it is not Angular Material. The CDK provides the interaction patterns, the accessibility infrastructure, and the low-level mechanics. It does not provide the visual design. When you use the CDK, you get a set of well-tested tools and you bring your own styles .
This distinction matters. Angular Material gives you a complete design system with buttons, tables, and dialogs that look like Material Design. The CDK gives you the behavior behind those components without the look. A cdk-table is a data table with sorting, pagination, and sticky headers — but no colors, no fonts, no spacing. You style it yourself. This makes the CDK the right tool when you need the behavior of a complex component but the design must match your application’s own visual language.
Key point: The CDK is organized into three categories: common behaviors (accessibility, drag-and-drop, overlay, scrolling), unstyled components (dialog, table, stepper, tree), and utilities (coercion, keycodes, testing) . Every component in Angular Material is built on top of these primitives. When you use the CDK directly, you are using the same foundation the Angular team uses.
Why the CDK exists
Angular Material ships with a complete design system. But not every application wants Material Design. Some have their own design system. Some need a component that Material does not provide. Some need the accessibility behavior of a Material component without the styling overhead.
The design system problem. A company with its own design language — a brand-specific button, a custom table style, a unique dialog layout — cannot use Angular Material’s components without overriding the styles. Overriding is possible but fragile. The CDK provides the behavior without the styles, so the component starts from a blank slate .
The accessibility problem. Building an accessible dialog, menu, or form control from scratch is difficult. The CDK provides FocusTrap, FocusMonitor, LiveAnnouncer, and ListKeyManager — the same tools Angular Material uses internally . A custom component built on these tools has the same accessibility guarantees as a Material component.
The composition problem. A component like a date picker needs an overlay to position the calendar, a list key manager to navigate the dates, a focus trap to keep focus inside the calendar, and a portal to render the calendar outside the component’s DOM hierarchy. The CDK provides each of these as a separate, composable primitive .
The trade-off. The CDK is lower-level than Angular Material. A cdk-table requires you to define the columns, the header rows, the cell templates, and the sorting logic. A mat-table does most of this for you. The CDK requires more code but gives more control. The choice depends on whether the control is needed.
a. Common Behaviors
The CDK’s common behaviors are the utilities that many components share. They solve problems that are not specific to any one component.
Accessibility (@angular/cdk/a11y) provides the tools for building accessible components. FocusMonitor reports how an element received focus — mouse, keyboard, touch, or programmatically . FocusTrap constrains Tab focus within an element, which is required for modal dialogs . LiveAnnouncer announces messages to screen readers using an ARIA live region . ListKeyManager manages keyboard navigation in lists that use role="menu" or role="listbox" .
Overlay (@angular/cdk/overlay) positions floating panels on the screen. It provides two positioning strategies: GlobalPositionStrategy for overlays positioned relative to the viewport (modals, notifications), and ConnectedPositionStrategy for overlays positioned relative to a trigger element (menus, tooltips, dropdowns) . The overlay is what makes mat-select, mat-menu, and mat-dialog work.
Portal (@angular/cdk/portal) renders dynamic content into a different part of the DOM. A Portal is a piece of UI — a component or a template — and a PortalOutlet is the slot where it is rendered. Overlays are built on portals: the overlay creates a PortalOutlet at the document body, and the content is attached as a Portal .
Drag and Drop (@angular/cdk/drag-drop) creates draggable elements and sortable lists. cdkDrag makes any element draggable. cdkDropList makes a list sortable. The package handles the preview, the placeholder, the boundary constraints, and the axis locking .
Scrolling (@angular/cdk/scrolling) provides virtual scrolling for large lists. cdk-virtual-scroll-viewport renders only the items that are visible, simulating the full list by making the container height match the total item count. This allows lists of 100,000 items to render without performance degradation .
Layout (@angular/cdk/layout) provides BreakpointObserver for responding to viewport size changes and media queries. It includes predefined breakpoints like Handset, Tablet, and Web, and allows custom media queries .
b. Unstyled Components
The CDK’s components are the behavior of Angular Material’s components without the styling. They are the same components, stripped of the Material Design tokens.
CdkTable (@angular/cdk/table) is the data table behavior. It provides the column definitions, the row definitions, the cell outlets, and the sticky header/footer support. It does not provide sorting or pagination — those are separate utilities that you integrate .
CdkDialog (@angular/cdk/dialog) is the modal dialog behavior. It provides the overlay, the focus trap, the backdrop, and the data injection. It does not provide the Material dialog’s header, content, and action sections .
CdkStepper (@angular/cdk/stepper) is the step-by-step workflow behavior. It provides the step definitions, the linear/non-linear navigation, the selection change events, and the error state. It does not provide the Material stepper’s visual indicators .
CdkMenu, CdkListbox, CdkTree, CdkAccordion are the other unstyled components. Each provides the behavior of its Material counterpart without the design .
c. Utilities
The CDK’s utilities are the helper functions that the components use internally.
Testing (@angular/cdk/testing) provides component test harnesses. A harness is a class that interacts with a component through its public API, not its DOM structure. This makes tests resilient to internal changes. The harnesses are used by Angular Material’s own tests and are available for custom components .
Coercion (@angular/cdk/coercion) provides functions for converting values to specific types. coerceBooleanProperty, coerceNumberProperty, and coerceStringArray are used by components that accept inputs in multiple formats .
Keycodes (@angular/cdk/keycodes) provides constants for keyboard keys. ENTER, ESCAPE, SPACE, and TAB are the most common. Using the constants instead of raw key codes makes the code more readable .
Collections (@angular/cdk/collections) provides DataSource, an abstract class for connecting data to a component. The connect method returns an observable of the data, and the disconnect method cleans up subscriptions. CdkTable and cdk-virtual-scroll-viewport both accept a DataSource .
Complete Example Session
This session builds a custom dropdown component using the CDK’s overlay, portal, and accessibility utilities.
// ============================================
// PART 1: THE IMPORTS
// ============================================
import { Component, inject, signal, viewChild, ElementRef } from '@angular/core';
import { OverlayModule, Overlay, ConnectedPosition } from '@angular/cdk/overlay';
import { A11yModule, FocusMonitor } from '@angular/cdk/a11y';
import { PortalModule } from '@angular/cdk/portal';
// ============================================
// PART 2: THE DROPDOWN COMPONENT
// ============================================
@Component({
selector: 'app-custom-dropdown',
imports: [OverlayModule, A11yModule, PortalModule],
template: `
<button
#trigger
type="button"
class="dropdown-trigger"
(click)="toggle()"
aria-haspopup="listbox"
[attr.aria-expanded]="isOpen()"
>
{{ selected() }}
<span class="chevron">▼</span>
</button>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="trigger"
[cdkConnectedOverlayOpen]="isOpen()"
[cdkConnectedOverlayPositions]="positions"
[cdkConnectedOverlayHasBackdrop]="true"
(backdropClick)="close()"
(detach)="close()"
>
<div class="dropdown-panel" role="listbox">
@for (option of options; track option) {
<div
role="option"
class="dropdown-option"
[class.selected]="option === selected()"
[attr.aria-selected]="option === selected()"
(click)="select(option)"
>
{{ option }}
</div>
}
</div>
</ng-template>
`,
styles: [`
.dropdown-trigger {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
background: white;
cursor: pointer;
min-width: 150px;
justify-content: space-between;
}
.dropdown-panel {
background: white;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
min-width: 150px;
padding: 4px 0;
}
.dropdown-option {
padding: 8px 12px;
cursor: pointer;
}
.dropdown-option:hover {
background: #f0f0f0;
}
.dropdown-option.selected {
background: #e3f2fd;
font-weight: 500;
}
`],
})
export class CustomDropdownComponent {
private focusMonitor = inject(FocusMonitor);
options = ['Option 1', 'Option 2', 'Option 3'];
isOpen = signal(false);
selected = signal('Select an option');
trigger = viewChild.required<ElementRef>('trigger');
// Position the panel below the trigger, or above if there is no room
positions: ConnectedPosition[] = [
{
originX: 'start',
originY: 'bottom',
overlayX: 'start',
overlayY: 'top',
offsetY: 4,
},
{
originX: 'start',
originY: 'top',
overlayX: 'start',
overlayY: 'bottom',
offsetY: -4,
},
];
toggle(): void {
this.isOpen.update((v) => !v);
}
close(): void {
this.isOpen.set(false);
// Restore focus to the trigger
this.trigger().nativeElement.focus();
}
select(option: string): void {
this.selected.set(option);
this.close();
}
}
The cdkConnectedOverlay directive creates the overlay and positions it relative to the trigger. The positions array defines the preferred positions. The cdkConnectedOverlayHasBackdrop option adds a backdrop that closes the dropdown when clicked. The cdkConnectedOverlayOpen binding controls visibility. The (detach) event fires when the overlay is closed by the user pressing Escape or clicking outside .
// ============================================
// PART 3: THE CUSTOM TABLE
// ============================================
import { Component, signal } from '@angular/core';
import { CdkTableModule } from '@angular/cdk/table';
import { CdkScrollableModule } from '@angular/cdk/scrolling';
interface User {
id: number;
name: string;
email: string;
role: string;
}
@Component({
selector: 'app-custom-table',
imports: [CdkTableModule, CdkScrollableModule],
template: `
<table cdk-table [dataSource]="users()" class="custom-table">
<ng-container cdkColumnDef="id">
<th cdk-header-cell *cdkHeaderCellDef>ID</th>
<td cdk-cell *cdkCellDef="let user">{{ user.id }}</td>
</ng-container>
<ng-container cdkColumnDef="name">
<th cdk-header-cell *cdkHeaderCellDef>Name</th>
<td cdk-cell *cdkCellDef="let user">{{ user.name }}</td>
</ng-container>
<ng-container cdkColumnDef="email">
<th cdk-header-cell *cdkHeaderCellDef>Email</th>
<td cdk-cell *cdkCellDef="let user">{{ user.email }}</td>
</ng-container>
<ng-container cdkColumnDef="role">
<th cdk-header-cell *cdkHeaderCellDef>Role</th>
<td cdk-cell *cdkCellDef="let user">{{ user.role }}</td>
</ng-container>
<tr cdk-header-row *cdkHeaderRowDef="displayedColumns"></tr>
<tr cdk-row *cdkRowDef="let row; columns: displayedColumns"></tr>
</table>
`,
styles: [`
.custom-table {
width: 100%;
border-collapse: collapse;
}
.custom-table th {
text-align: left;
padding: 12px 16px;
border-bottom: 2px solid #e0e0e0;
font-weight: 600;
}
.custom-table td {
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
}
.custom-table tr:hover td {
background: #fafafa;
}
`],
})
export class CustomTableComponent {
displayedColumns = ['id', 'name', 'email', 'role'];
users = signal<User[]>([
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin' },
{ id: 2, name: 'Bob Smith', email: 'bob@example.com', role: 'User' },
{ id: 3, name: 'Carol Williams', email: 'carol@example.com', role: 'Editor' },
]);
}
The cdk-table provides the data table behavior: the column definitions, the row definitions, and the cell outlets. The cdkColumnDef defines each column. The cdkHeaderCellDef and cdkCellDef define the header and cell templates. The cdkHeaderRowDef and cdkRowDef define the rows .
// ============================================
// PART 4: THE VIRTUAL SCROLL LIST
// ============================================
import { Component } from '@angular/core';
import { ScrollingModule } from '@angular/cdk/scrolling';
@Component({
selector: 'app-virtual-list',
imports: [ScrollingModule],
template: `
<cdk-virtual-scroll-viewport itemSize="50" class="viewport">
<div *cdkVirtualFor="let item of items" class="item">
{{ item }}
</div>
</cdk-virtual-scroll-viewport>
`,
styles: [`
.viewport {
height: 300px;
width: 100%;
border: 1px solid #ccc;
}
.item {
height: 50px;
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid #f0f0f0;
}
`],
})
export class VirtualListComponent {
items = Array.from({ length: 100000 }, (_, i) => `Item #${i}`);
}
The cdk-virtual-scroll-viewport renders only the visible items. The itemSize property must match the actual height of each item. The *cdkVirtualFor replaces *ngFor inside the viewport .
// ============================================
// PART 5: THE RESPONSIVE LAYOUT
// ============================================
import { Component, inject } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { toSignal } from '@angular/core/rxjs-interop';
import { map } from 'rxjs';
@Component({
selector: 'app-responsive-layout',
template: `
<div class="layout" [class.mobile]="isMobile()">
@if (!isMobile()) {
<aside class="sidebar">
<p>Sidebar</p>
</aside>
}
<main class="content">
<p>Main content</p>
</main>
</div>
`,
styles: [`
.layout {
display: flex;
height: 100vh;
}
.layout.mobile {
flex-direction: column;
}
.sidebar {
width: 250px;
background: #f5f5f5;
padding: 16px;
}
.content {
flex: 1;
padding: 16px;
}
`],
})
export class ResponsiveLayoutComponent {
private breakpointObserver = inject(BreakpointObserver);
isMobile = toSignal(
this.breakpointObserver
.observe([Breakpoints.Handset])
.pipe(map((result) => result.matches)),
{ initialValue: false }
);
}
The BreakpointObserver observes the Handset breakpoint, which matches mobile devices. The toSignal function converts the observable to a signal that the template can read .
// ============================================
// PART 6: THE DRAG AND DROP LIST
// ============================================
import { Component, signal } from '@angular/core';
import { DragDropModule, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
@Component({
selector: 'app-drag-list',
imports: [DragDropModule],
template: `
<div cdkDropList class="list" (cdkDropListDropped)="drop($event)">
@for (item of items(); track item) {
<div cdkDrag class="item">
{{ item }}
<div *cdkDragPlaceholder class="placeholder"></div>
</div>
}
</div>
`,
styles: [`
.list {
width: 300px;
border: 1px solid #ccc;
border-radius: 4px;
}
.item {
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
background: white;
cursor: grab;
}
.item:active {
cursor: grabbing;
}
.placeholder {
background: #e3f2fd;
height: 48px;
}
`],
})
export class DragListComponent {
items = signal(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
drop(event: CdkDragDrop<string[]>): void {
const current = this.items();
const updated = [...current];
moveItemInArray(updated, event.previousIndex, event.currentIndex);
this.items.set(updated);
}
}
The cdkDropList directive creates a sortable list. The cdkDrag directive makes each item draggable. The cdkDropListDropped event fires when an item is dropped, and moveItemInArray reorders the array .
// ============================================
// PART 7: THE CLIPBOARD DIRECTIVE
// ============================================
import { Component } from '@angular/core';
import { ClipboardModule } from '@angular/cdk/clipboard';
@Component({
selector: 'app-copy-button',
imports: [ClipboardModule],
template: `
<button [cdkCopyToClipboard]="textToCopy" (cdkCopyToClipboardCopied)="onCopied($event)">
Copy to Clipboard
</button>
@if (copied) {
<span>Copied!</span>
}
`,
})
export class CopyButtonComponent {
textToCopy = 'Hello from the CDK clipboard!';
copied = false;
onCopied(success: boolean): void {
this.copied = success;
setTimeout(() => (this.copied = false), 2000);
}
}
The cdkCopyToClipboard directive copies the bound text to the system clipboard. The cdkCopyToClipboardCopied event fires with a boolean indicating success .
// ============================================
// PART 8: THE TEST HARNESS
// ============================================
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { CustomDropdownComponent } from './custom-dropdown.component';
describe('CustomDropdownComponent', () => {
let fixture: ComponentFixture<CustomDropdownComponent>;
let loader: HarnessLoader;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CustomDropdownComponent],
}).compileComponents();
fixture = TestBed.createComponent(CustomDropdownComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
fixture.detectChanges();
});
it('should open the dropdown', () => {
const trigger = fixture.nativeElement.querySelector('button');
trigger.click();
fixture.detectChanges();
expect(fixture.componentInstance.isOpen()).toBe(true);
});
});
The TestbedHarnessEnvironment provides a loader for test harnesses. The harness interacts with the component through its public API .
// ============================================
// PART 9: THE MODULE IMPORTS
// ============================================
// The CDK is imported in pieces. Each package has its own module.
import { OverlayModule } from '@angular/cdk/overlay';
import { A11yModule } from '@angular/cdk/a11y';
import { PortalModule } from '@angular/cdk/portal';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { LayoutModule } from '@angular/cdk/layout';
import { CdkTableModule } from '@angular/cdk/table';
import { DialogModule } from '@angular/cdk/dialog';
import { ClipboardModule } from '@angular/cdk/clipboard';
import { CdkStepperModule } from '@angular/cdk/stepper';
// Import only what you use. The CDK is tree-shakeable.
Each CDK package is imported separately. The build only includes the code for the imported modules. Unused packages are removed by the bundler .
// ============================================
// PART 10: THE DECISION MATRIX
// ============================================
// Use Angular Material when:
// - The component exists in Material
// - The Material Design look is acceptable
// - You want the Angular team to maintain the UI
// Use the CDK when:
// - The component does not exist in Material
// - The visual design must be custom
// - You need the behavior without the styling
// - You are building a design system
// Use a custom component from scratch when:
// - The interaction pattern is application-specific
// - The CDK primitives are insufficient
// - The component is simple enough to build directly
The decision is about control and reuse. Angular Material provides the most reuse with the least control. The CDK provides the behavior with full control over the design. A custom component provides the most control with the most code to maintain.
Quick Reference
The CDK Categories
| Category | Package | Purpose |
|---|---|---|
| Accessibility | @angular/cdk/a11y | Focus, announcements, keyboard nav |
| Overlay | @angular/cdk/overlay | Floating panels, popovers |
| Portal | @angular/cdk/portal | Dynamic content rendering |
| Drag and Drop | @angular/cdk/drag-drop | Sortable lists, draggable elements |
| Scrolling | @angular/cdk/scrolling | Virtual scrolling, scroll detection |
| Layout | @angular/cdk/layout | Breakpoint detection |
| Table | @angular/cdk/table | Data table behavior |
| Dialog | @angular/cdk/dialog | Modal dialog behavior |
| Clipboard | @angular/cdk/clipboard | Copy to clipboard |
| Stepper | @angular/cdk/stepper | Step-by-step workflows |
The A11y Utilities
| Utility | Purpose |
|---|---|
FocusMonitor | Detect focus origin (mouse, keyboard, touch) |
FocusTrap | Constrain Tab focus within an element |
LiveAnnouncer | Announce messages to screen readers |
ListKeyManager | Keyboard navigation in lists |
InteractivityChecker | Check focusable, visible, enabled states |
The Overlay Position Strategies
| Strategy | Purpose |
|---|---|
GlobalPositionStrategy | Position relative to viewport |
ConnectedPositionStrategy | Position relative to a trigger |
FlexibleConnectedPositionStrategy | Advanced positioning with fallbacks |
The Virtual Scroll Properties
| Property | Purpose |
|---|---|
itemSize | Height of each item in pixels |
minBufferPx | Minimum buffer before rendering more |
maxBufferPx | Maximum buffer to render |
orientation | vertical or horizontal |
The Drag and Drop Directives
| Directive | Purpose |
|---|---|
cdkDrag | Make an element draggable |
cdkDropList | Make a list sortable |
cdkDragHandle | Restrict dragging to a handle |
cdkDragPreview | Custom drag preview |
cdkDragPlaceholder | Custom placeholder |
Best Practices
✅ Do This:
// Import only the CDK packages you use
import { OverlayModule } from '@angular/cdk/overlay'; // ✅
// Use FocusMonitor to detect keyboard focus
this.focusMonitor.monitor(elementRef).subscribe(origin => { }); // ✅
// Use LiveAnnouncer for dynamic messages
this.liveAnnouncer.announce('Saved', 'polite'); // ✅
// Use virtual scrolling for large lists
<cdk-virtual-scroll-viewport itemSize="50"> // ✅
// Use BreakpointObserver for responsive layouts
this.breakpointObserver.observe([Breakpoints.Handset]); // ✅
❌ Don’t Do This:
// Don't import the entire CDK
import { } from '@angular/cdk'; // no barrel import // ❌
// Don't use cdkDrag without a drop list for sorting
<div cdkDrag> // free drag only // ❌
// Don't forget the overlay prebuilt styles
@import '@angular/cdk/overlay-prebuilt.css'; // ❌ required
// Don't skip the test harness for complex components
// The harness makes tests resilient to DOM changes. // ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Overlay not positioned | Missing overlay prebuilt styles | Import overlay-prebuilt.css |
| Virtual scroll items misaligned | Wrong itemSize | Match itemSize to actual height |
| Drag and drop not working | Missing cdkDropList | Wrap items in a drop list |
| Focus trap not working | Missing cdkFocusInitial | Add the attribute to the initial focus element |
| Breakpoint not detecting | Wrong breakpoint constant | Check the Breakpoints values |
| Table not rendering | Missing column definitions | Add cdkColumnDef for each column |
Real-World Examples
1. Overlay
const overlayRef = overlay.create();
overlayRef.attach(new ComponentPortal(MyComponent));
2. Portal
<ng-template cdkPortal>Content</ng-template>
3. Focus Trap
<div cdkTrapFocus [cdkTrapFocusAutoCapture]="true">...</div>
4. Live Announcer
liveAnnouncer.announce('Changes saved', 'polite');
5. Virtual Scroll
<cdk-virtual-scroll-viewport itemSize="50">...</cdk-virtual-scroll-viewport>
6. Drag and Drop
<div cdkDropList (cdkDropListDropped)="drop($event)">...</div>
7. Breakpoint Observer
breakpointObserver.observe([Breakpoints.Handset]).subscribe(...);
8. CDK Table
<table cdk-table [dataSource]="data">...</table>
9. Clipboard
<button [cdkCopyToClipboard]="text">Copy</button>
10. Dialog
const dialogRef = dialog.open(MyDialogComponent);
Visual
The CDK Categories
┌──────────────────────────────────────────────┐
│ COMMON BEHAVIORS │
│ a11y, bidi, clipboard, drag-drop, layout, │
│ observers, overlay, platform, portal, │
│ scrolling, text-field │
│ │
│ UNSTYLED COMPONENTS │
│ accordion, dialog, listbox, menu, stepper, │
│ table, tree │
│ │
│ UTILITIES │
│ coercion, collections, keycodes, testing │
│ │
└──────────────────────────────────────────────┘
The Overlay Architecture
┌──────────────────────────────────────────────┐
│ OVERLAY │
│ OverlayRef │
│ ├─ PositionStrategy │
│ │ ├─ GlobalPositionStrategy │
│ │ └─ ConnectedPositionStrategy │
│ ├─ ScrollStrategy │
│ │ ├─ NoopScrollStrategy │
│ │ ├─ CloseScrollStrategy │
│ │ ├─ BlockScrollStrategy │
│ │ └─ RepositionScrollStrategy │
│ └─ PortalOutlet │
│ └─ ComponentPortal / TemplatePortal│
│ │
└──────────────────────────────────────────────┘
The Virtual Scroll
┌──────────────────────────────────────────────┐
│ VIRTUAL SCROLL │
│ │
│ Total items: 100,000 │
│ Visible items: ~10 │
│ Rendered items: ~20 (with buffer) │
│ │
│ The viewport height matches the total │
│ item count. Only the visible items are │
│ rendered. Scrolling shifts which items │
│ are rendered. │
│ │
└──────────────────────────────────────────────┘
The Responsive Breakpoints
┌──────────────────────────────────────────────┐
│ BREAKPOINTS │
│ │
│ Handset: (max-width: 599.98px) portrait │
│ (max-width: 959.98px) landscape │
│ │
│ Tablet: (min-width: 600px) portrait │
│ (min-width: 960px) landscape │
│ │
│ Web: (min-width: 1024px) │
│ │
│ Use BreakpointObserver to react to changes. │
│ │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| CDK definition | Component Dev Kit — behavior primitives |
| Relationship to Material | Foundation; Material adds styling |
| Categories | Common behaviors, unstyled components, utilities |
| Accessibility | FocusMonitor, FocusTrap, LiveAnnouncer, ListKeyManager |
| Overlay | Floating panels, position strategies |
| Portal | Dynamic content rendering |
| Drag and Drop | Sortable lists, draggable elements |
| Scrolling | Virtual scrolling for large lists |
| Layout | Breakpoint detection |
| Table | Data table behavior |
| Testing | Component test harnesses |
| Installation | npm install @angular/cdk |
Key takeaways:
- The CDK is a set of behavior primitives, not a design system. It provides the interaction patterns, accessibility infrastructure, and low-level mechanics that Angular Material is built on. It does not provide the visual design .
- The CDK is organized into three categories: common behaviors, unstyled components, and utilities. The common behaviors include accessibility, overlay, portal, drag-and-drop, scrolling, and layout. The unstyled components include dialog, table, stepper, and tree. The utilities include coercion, keycodes, collections, and testing .
- The accessibility package is the foundation for building accessible components.
FocusMonitordetects how an element received focus.FocusTrapconstrains Tab focus in modals.LiveAnnouncerannounces messages to screen readers.ListKeyManagerhandles keyboard navigation . - The overlay package positions floating panels. It provides global positioning for modals and connected positioning for menus, tooltips, and dropdowns. The overlay is built on portals, which render content into a different part of the DOM .
- Virtual scrolling renders only visible items.
cdk-virtual-scroll-viewportallows lists of 100,000 items to render without performance degradation. TheitemSizeproperty must match the actual height of each item . - The CDK is tree-shakeable. Import only the packages you use. The build includes only the code for the imported modules. Unused packages are removed by the bundler.
- The CDK is the same foundation Angular Material uses. When you use the CDK directly, you are using the same tools the Angular team uses to build the Material components. The difference is that you provide the styling.
Remember: The Angular CDK is the behavior layer. Angular Material is the design layer. The CDK gives you the overlay, the focus trap, the virtual scroll, the drag-and-drop, and the accessibility tools. It does not give you the colors, the fonts, or the spacing. When you need the behavior but not the design, the CDK is the tool. When you need both, Angular Material is the tool. The choice is about control and reuse. The CDK provides more control with more code. Material provides more reuse with less control.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!