Run the CLI
Use the CLI to add the component to your project.
npx @ngzard/ui@latest add breadcrumbDisplays the path to the current resource using a hierarchy of links.
import { Component } from '@angular/core';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardBreadcrumbModule } from '../breadcrumb.module';
@Component({
selector: 'z-demo-breadcrumb-default',
imports: [ZardBreadcrumbModule, ZardIconComponent],
standalone: true,
template: `
<z-breadcrumb zWrap="wrap" zAlign="start">
<z-breadcrumb-item [routerLink]="['/']">
<z-icon zType="house" />
Home
</z-breadcrumb-item>
<z-breadcrumb-item [routerLink]="['/docs/components']">Components</z-breadcrumb-item>
<z-breadcrumb-item>Breadcrumb</z-breadcrumb-item>
</z-breadcrumb>
`,
})
export class ZardDemoBreadcrumbDefaultComponent {}
Use the CLI to add the component to your project.
npx @ngzard/ui@latest add breadcrumbpnpm dlx @ngzard/ui@latest add breadcrumbyarn dlx @ngzard/ui@latest add breadcrumbbunx @ngzard/ui@latest add breadcrumbCreate the component directory structure and add the following files to your project.
import {
ChangeDetectionStrategy,
Component,
computed,
contentChild,
contentChildren,
inject,
input,
TemplateRef,
ViewEncapsulation,
} from '@angular/core';
import { Params, RouterLink } from '@angular/router';
import type { ClassValue } from 'clsx';
import {
breadcrumbEllipsisVariants,
breadcrumbItemVariants,
breadcrumbListVariants,
breadcrumbVariants,
ZardBreadcrumbAlignVariants,
ZardBreadcrumbEllipsisColorVariants,
ZardBreadcrumbSizeVariants,
ZardBreadcrumbWrapVariants,
} from './breadcrumb.variants';
import { mergeClasses } from '../../shared/utils/utils';
import { ZardStringTemplateOutletDirective } from '../core/directives/string-template-outlet/string-template-outlet.directive';
import { ZardIconComponent } from '../icon/icon.component';
@Component({
selector: 'z-breadcrumb-ellipsis, [z-breadcrumb-ellipsis]',
imports: [ZardIconComponent],
template: ` <z-icon zType="ellipsis" /> `,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': 'classes()',
'aria-hidden': 'true',
role: 'presentation',
},
exportAs: 'zBreadcrumbEllipsis',
})
export class ZardBreadcrumbEllipsisComponent {
readonly zColor = input<ZardBreadcrumbEllipsisColorVariants>('muted');
readonly class = input<ClassValue>('');
protected readonly classes = computed(() =>
mergeClasses(breadcrumbEllipsisVariants({ zColor: this.zColor() }), this.class()),
);
}
@Component({
selector: 'z-breadcrumb-item, [z-breadcrumb-item]',
imports: [ZardStringTemplateOutletDirective, ZardIconComponent, RouterLink],
template: `
<ng-template #itemContent><ng-content /></ng-template>
<li [class]="classes()">
@if (isEllipsis()) {
<ng-container *zStringTemplateOutlet="itemContent" />
} @else {
<a
class="flex items-center gap-1.5"
[routerLink]="routerLink()"
[queryParams]="queryParams()"
[fragment]="fragment()"
>
<ng-container *zStringTemplateOutlet="itemContent" />
</a>
}
</li>
@if (!isLast()) {
<li aria-hidden="true" role="presentation" [class]="separatorClasses()" (click)="$event.stopPropagation()">
@if (isTemplate(separator())) {
<ng-container *zStringTemplateOutlet="separator()" />
} @else if (separator()) {
{{ separator() }}
} @else {
<z-icon zType="chevron-right" />
}
</li>
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
class: 'inline-flex items-center gap-1.5',
},
hostDirectives: [
{
directive: RouterLink,
inputs: [
'routerLink',
'queryParams',
'fragment',
'queryParamsHandling',
'state',
'relativeTo',
'preserveFragment',
'skipLocationChange',
'replaceUrl',
],
},
],
exportAs: 'zBreadcrumbItem',
})
export class ZardBreadcrumbItemComponent {
private readonly breadcrumbComponent = inject(ZardBreadcrumbComponent);
private readonly content = contentChild(ZardBreadcrumbEllipsisComponent);
/*
These three inputs are affecting the link so we need them for anchor link.
They are not part of component API in any sense as that is done through
host directive.
*/
readonly routerLink = input<string[]>([]);
readonly queryParams = input<Params | null | undefined>();
readonly fragment = input<string | undefined>();
readonly class = input<ClassValue>('');
protected readonly separator = computed(() => this.breadcrumbComponent.zSeparator());
protected readonly isLast = computed<boolean>(() => this === this.breadcrumbComponent.items().at(-1));
protected readonly isEllipsis = computed<boolean>(() => this.content() !== undefined);
protected readonly classes = computed(() => mergeClasses(breadcrumbItemVariants(), this.class()));
protected readonly separatorClasses = computed(() => 'text-muted-foreground [&_svg]:size-3.5');
protected isTemplate(value: string | TemplateRef<void>): value is TemplateRef<void> {
return value instanceof TemplateRef;
}
}
@Component({
selector: 'z-breadcrumb, [z-breadcrumb]',
template: `
<nav aria-label="breadcrumb" [class]="navClasses()">
<ol [class]="listClasses()">
<ng-content />
</ol>
</nav>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
exportAs: 'zBreadcrumb',
})
export class ZardBreadcrumbComponent {
readonly zSize = input<ZardBreadcrumbSizeVariants>('md');
readonly zAlign = input<ZardBreadcrumbAlignVariants>('start');
readonly zWrap = input<ZardBreadcrumbWrapVariants>('wrap');
readonly zSeparator = input<string | TemplateRef<void>>('');
readonly class = input<ClassValue>('');
readonly items = contentChildren(ZardBreadcrumbItemComponent);
protected readonly navClasses = computed(() =>
mergeClasses(breadcrumbVariants({ zSize: this.zSize() }), this.class()),
);
protected readonly listClasses = computed(() =>
breadcrumbListVariants({ zAlign: this.zAlign(), zWrap: this.zWrap() }),
);
}
import { cva, type VariantProps } from 'class-variance-authority';
export const breadcrumbVariants = cva('w-full', {
variants: {
zSize: {
sm: 'text-xs',
md: 'text-sm',
lg: 'text-base',
},
},
defaultVariants: {
zSize: 'md',
},
});
export type ZardBreadcrumbSizeVariants = NonNullable<VariantProps<typeof breadcrumbVariants>['zSize']>;
export const breadcrumbListVariants = cva(
'text-muted-foreground flex flex-wrap items-center gap-1.5 wrap-break-word sm:gap-2.5',
{
variants: {
zAlign: {
start: 'justify-start',
center: 'justify-center',
end: 'justify-end',
},
zWrap: {
wrap: 'flex-wrap',
nowrap: 'flex-nowrap',
},
},
defaultVariants: {
zAlign: 'start',
zWrap: 'wrap',
},
},
);
export type ZardBreadcrumbAlignVariants = NonNullable<VariantProps<typeof breadcrumbListVariants>['zAlign']>;
export type ZardBreadcrumbWrapVariants = NonNullable<VariantProps<typeof breadcrumbListVariants>['zWrap']>;
export const breadcrumbItemVariants = cva(
'inline-flex items-center gap-1.5 transition-colors cursor-pointer hover:text-foreground last:text-foreground last:font-normal last:pointer-events-none',
);
export type ZardBreadcrumbItemVariants = VariantProps<typeof breadcrumbItemVariants>;
export const breadcrumbEllipsisVariants = cva('flex', {
variants: {
zColor: {
muted: 'text-muted-foreground',
strong: 'text-foreground',
},
},
defaultVariants: {
zColor: 'muted',
},
});
export type ZardBreadcrumbEllipsisColorVariants = NonNullable<
VariantProps<typeof breadcrumbEllipsisVariants>['zColor']
>;
import { NgModule } from '@angular/core';
import {
ZardBreadcrumbComponent,
ZardBreadcrumbEllipsisComponent,
ZardBreadcrumbItemComponent,
} from './breadcrumb.component';
const components = [ZardBreadcrumbComponent, ZardBreadcrumbItemComponent, ZardBreadcrumbEllipsisComponent];
@NgModule({
imports: components,
exports: components,
})
export class ZardBreadcrumbModule {}
import { Component } from '@angular/core';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardBreadcrumbModule } from '../breadcrumb.module';
@Component({
selector: 'z-demo-breadcrumb-default',
imports: [ZardBreadcrumbModule, ZardIconComponent],
standalone: true,
template: `
<z-breadcrumb zWrap="wrap" zAlign="start">
<z-breadcrumb-item [routerLink]="['/']">
<z-icon zType="house" />
Home
</z-breadcrumb-item>
<z-breadcrumb-item [routerLink]="['/docs/components']">Components</z-breadcrumb-item>
<z-breadcrumb-item>Breadcrumb</z-breadcrumb-item>
</z-breadcrumb>
`,
})
export class ZardDemoBreadcrumbDefaultComponent {}
import { Component } from '@angular/core';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardBreadcrumbModule } from '../breadcrumb.module';
@Component({
selector: 'z-demo-breadcrumb-separator',
imports: [ZardBreadcrumbModule, ZardIconComponent],
standalone: true,
template: `
<z-breadcrumb [zSeparator]="customSeparator">
<z-breadcrumb-item>Home</z-breadcrumb-item>
<z-breadcrumb-item>Components</z-breadcrumb-item>
<z-breadcrumb-item>Breadcrumb</z-breadcrumb-item>
</z-breadcrumb>
<ng-template #customSeparator>
<z-icon zType="arrow-right" />
</ng-template>
`,
})
export class ZardDemoBreadcrumbSeparatorComponent {}
import { Component } from '@angular/core';
import { ZardMenuModule } from '../../menu/menu.module';
import { ZardBreadcrumbModule } from '../breadcrumb.module';
@Component({
selector: 'z-demo-breadcrumb-ellipsis',
imports: [ZardBreadcrumbModule, ZardMenuModule],
standalone: true,
template: `
<z-breadcrumb>
<z-breadcrumb-item [routerLink]="['/']">Home</z-breadcrumb-item>
<z-breadcrumb-item>
<z-breadcrumb-ellipsis z-menu [zMenuTriggerFor]="ellipsisMenu" />
<ng-template #ellipsisMenu>
<div z-menu-content class="w-48">
<button type="button" z-menu-item>Getting Started</button>
<button type="button" z-menu-item>Installation</button>
</div>
</ng-template>
</z-breadcrumb-item>
<z-breadcrumb-item>Components</z-breadcrumb-item>
<z-breadcrumb-item>Breadcrumb</z-breadcrumb-item>
</z-breadcrumb>
`,
})
export class ZardDemoBreadcrumbEllipsisComponent {}