Prog blog

Sticky header in Angular

Sticky header in Angular

Learn how to quickly and easily create a sticky header in your project. It's a quite popular UI pattern that helps you get back to the page header quickly when you're out of sight. The solution shown here has been simplified to show you the essence of the topic.

First we create a header navigation component.

ng generate component header-nav --module=app

In it we create observable scrollUp$ which will only work if the code is run in the browser, then listens to the scroll event with throttle 50 ms because we don't need much precision here. In the next step, it maps the event to the value of window.scrollY and connects us a sequence of events in pairs and based on these two values we can determine whether the user scrolls up the page.

header-nav.component.ts

import {
  ChangeDetectionStrategy,
  Component,
  Inject,
  PLATFORM_ID,
} from '@angular/core';
import { filter, map, pairwise, throttleTime, switchMap } from 'rxjs/operators';

import { fromEvent, of } from 'rxjs';
import { isPlatformBrowser } from '@angular/common';

@Component({
  selector: 'app-header-nav',
  templateUrl: './header-nav.component.html',
  styleUrls: ['./header-nav.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HeaderNavComponent {
  scrollUp$ = of(isPlatformBrowser(this.platformId)).pipe(
    filter(isBrowser => isBrowser),
    switchMap(() => fromEvent(document, 'scroll')),
    throttleTime(50),
    map(() => window.scrollY),
    pairwise(),
    map(([prevScrollY, currentScrollY]) => prevScrollY > currentScrollY)
  );

  constructor(@Inject(PLATFORM_ID) private platformId: object) {}
}

Then we create html of our navigation. To ensure that our site does not jump when showing and hiding the navigation, we will create two identical mat-toolbar with a copy of the ng-template #nav content. The only difference is *ngIf="scrollUp$ | async". The second navigation will only appear in the DOM when the user scrolls up the page. Class .fixed-nav sets the second element to the fixed position at the top of the screen.

header-nav.component.html

<ng-template #nav>
  <a
    mat-button
    routerLink="/"
    class="home-button"
    title="Home page of Prog blog"
  >
    <mat-icon inline="true" aria-label="Home page">home</mat-icon>
    <span>Prog blog</span>
  </a>
</ng-template>

<mat-toolbar color="primary">
  <ng-container *ngTemplateOutlet="nav"></ng-container>
</mat-toolbar>

<mat-toolbar *ngIf="scrollUp$ | async" class="fixed-nav" color="primary">
  <ng-container *ngTemplateOutlet="nav"></ng-container>
</mat-toolbar>

Example .fixed-nav

header-nav.component.scss

... 

.fixed-nav {
  position: fixed;
  top: 0;
  z-index: 1000;
}

And this is the end result of our navigation on the phone.

As a task you can add a quick animation of the appearance and disappearance to the navigation.