angular/components · error
A drawer was already declared for 'position="${position}"'
Error message
A drawer was already declared for 'position="${position}"' What it means
MatDrawerContainer validates its drawers in `_validateDrawers` and throws via `throwMatDuplicatedDrawerError` when two `<mat-drawer>` elements declare the same `position` ('start' or 'end'). Each side of a container may hold at most one drawer. It is a template-configuration error caught immediately at render time.
Source
Thrown at src/material/sidenav/drawer.ts:55
OnDestroy,
Output,
QueryList,
Renderer2,
ViewChild,
ViewEncapsulation,
DOCUMENT,
signal,
} from '@angular/core';
import {merge, Observable, Subject} from 'rxjs';
import {debounceTime, delay, filter, map, mapTo, startWith, take, takeUntil} from 'rxjs/operators';
import {_animationsDisabled} from '../core';
/**
* Throws an exception when two MatDrawer are matching the same position.
* @docs-private
*/
export function throwMatDuplicatedDrawerError(position: string) {
throw Error(`A drawer was already declared for 'position="${position}"'`);
}
/** Options for where to set focus to automatically on dialog open */
export type AutoFocusTarget = 'dialog' | 'first-tabbable' | 'first-heading';
/** Result of the toggle promise that indicates the state of the drawer. */
export type MatDrawerToggleResult = 'open' | 'close';
/** Drawer and SideNav display modes. */
export type MatDrawerMode = 'over' | 'push' | 'side';
/** Configures whether drawers should use auto sizing by default. */
export const MAT_DRAWER_DEFAULT_AUTOSIZE = new InjectionToken<boolean>(
'MAT_DRAWER_DEFAULT_AUTOSIZE',
{
providedIn: 'root',
factory: () => false,
},View on GitHub (pinned to 0411926e7d)
Solutions
- Give each drawer in the container a unique position: one 'start', one 'end'.
- Move the extra drawer into its own `<mat-drawer-container>` if two same-side drawers are needed.
- If position is bound with `[position]`, ensure the bound values cannot collide at runtime.
- Wrap drawer in `<mat-drawer-content>` usage checks and re-render after fixing the template.
Example fix
// before <mat-drawer-container> <mat-drawer position="start">Menu</mat-drawer> <mat-drawer position="start">Filters</mat-drawer> </mat-drawer-container> // after <mat-drawer-container> <mat-drawer position="start">Menu</mat-drawer> <mat-drawer position="end">Filters</mat-drawer> </mat-drawer-container>
Defensive patterns
Strategy: validation
Validate before calling
// Template review guard: count drawers per position
const positions = Array.from(
document.querySelectorAll('mat-drawer')
).map(d => d.getAttribute('ng-reflect-position'));
const dupes = positions.filter((p, i) => positions.indexOf(p) !== i);
if (dupes.length) throw new Error('Duplicate drawer position: ' + dupes[0]); Prevention
- One 'start' and at most one 'end' drawer per container.
- Avoid binding [position] to dynamic values that can collide.
- Lint templates for repeated <mat-drawer> blocks after copy-paste.
When it happens
Trigger: Declaring two `<mat-drawer position="start">` (or two 'end') inside one `<mat-drawer-container>`, or binding `position` to a property whose value collides with another drawer's.
Common situations: Copy-pasting a drawer block in a template and forgetting to change its position; conditional rendering where two drawers briefly both compute the same position; refactoring from two containers into one.
Related errors
- mat-form-field must contain a MatFormFieldControl.
- Invalid slider thumb input configuration! Valid configur
- MatDatepicker: No provider found for MAT_DATE_FORMATS. You m
- MatDatepicker: No provider found for DateAdapter. You must a
- MatDatepicker: No provider found for MAT_DATE_FORMATS. You m
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/cf1e70d7f7db64bf.
Report an issue: GitHub.