SPFx Application Customizer Example

A few days ago, I started working on SharePoint Framework (SPFx) Application Customizer extensions and tried out different ways to customize the top and bottom placeholders in SharePoint.

While working on those examples, I also created a useful extension displaying multiple time zone clocks in the SharePoint site header using an SPFx Application Customizer. This customisation helps everyone quickly see the local time of each location right from the SharePoint header, without opening any other app or tab.

SPFx Application Customizer Example

In this article, I will explain how to customise the SharePoint site header with multiple time-zone analog clocks using the SPFx application customizer extension.

Customize SharePoint Site Header with Multiple Time Zone Clocks Using SPFx

In the SharePoint framework, the application customizer extension allows us to customise the SharePoint site header and footer sections. Follow the steps below to display the various time zones on the analog clocks in the SharePoint site header.

I hope you have a SPFx development environment ready.

  1. Open the command prompt. Create a directory for the SPFx solution and navigate to it.
md SPFxAppExtTimeZones

cd SPFxAppExtTimeZones
  1. Run Yeoman SharePoint Generator to create the solution.
yo @microsoft/sharepoint
  1. The Yeoman generator will present you with a wizard that asks questions about the solution you’re creating.
    • What is your solution name? SPFxAppExtTimeZones
    • Which type of client-side component to create? Extension
    • Which type of client-side extension to create? Application Customizer
    • Add new Application Customizer to solution sp-fx-app-ext-time-zones.
    • What is your Application Customizer name? TimeZonesAppExt
spfx application customizer clock example

Note: Here, the solution is set to default to “No JavaScript framework.” If you want to use it with React, please download the React framework into your solution and start working on it.

  1. Once the solution is successfully created, you can see the folder structure shown in the image below. Add the code below to the .ts file in the src folder.
Implementing a World Clocks web part with SharePoint Framework
import { Log } from '@microsoft/sp-core-library';
import {
  BaseApplicationCustomizer,PlaceholderContent,
  PlaceholderName
} from '@microsoft/sp-application-base';
const LOG_SOURCE: string = 'TimeZonesAppExtApplicationCustomizer';

export interface ITimeZonesAppExtApplicationCustomizerProperties {
  testMessage: string;
}

/** A Custom Action which can be run during execution of a Client Side Application */
export default class TimeZonesAppExtApplicationCustomizer
  extends BaseApplicationCustomizer<ITimeZonesAppExtApplicationCustomizerProperties> {
  private _topPlaceholder: PlaceholderContent | undefined;
  public onInit(): Promise<void> {
    Log.info(LOG_SOURCE, `Initialized ${LOG_SOURCE}`);

    this.context.placeholderProvider.changedEvent.add(this, this._renderPlaceHolders);
    this._renderPlaceHolders();
    return Promise.resolve();
  }
  private _renderPlaceHolders(): void {
    // Check if top placeholder is available
    if (!this._topPlaceholder) {
      this._topPlaceholder = this.context.placeholderProvider.tryCreateContent(
        PlaceholderName.Top,
        { onDispose: this._onDispose }
      );

      if (!this._topPlaceholder) {
        console.error("The top placeholder was not found.");
        return;
      }

      // Inject HTML
      this._topPlaceholder.domElement.innerHTML = `
        <div id="multiClockBar" style="background:#f3f2f1; padding:6px; display:flex; justify-content:center; gap:30px; border-bottom:1px solid #ddd;">
        </div>
      `;

      // Add clocks
      const cities = [
        { name: 'New York', zone: 'America/New_York', abbr: 'EST' },
        { name: 'London', zone: 'Europe/London', abbr: 'GMT' },
        { name: 'Dubai', zone: 'Asia/Dubai', abbr: 'GST' },
        { name: 'Bangalore', zone: 'Asia/Kolkata', abbr: 'IST' }
      ];

      cities.forEach(city => this._createClock(city));
    }
  }
  private _createClock(city: { name: string; zone: string; abbr: string }) {
    const container = document.getElementById("multiClockBar");
    if (!container) return;

    const clockId = `clock-${city.name.replace(/\s/g, '')}`;
    const clockHtml = `
      <div style="text-align:center;">
        <canvas id="${clockId}" width="60" height="60" style="border:1px solid #ccc; border-radius:50%; background:white;"></canvas>
        <div style="font-size:12px; font-weight:bold;">${city.name}</div>
        <div style="font-size:11px; color:#555;">${city.abbr}</div>
      </div>
    `;
    container.insertAdjacentHTML("beforeend", clockHtml);

    this._drawAnalogClock(clockId, city.zone);
    setInterval(() => this._drawAnalogClock(clockId, city.zone), 1000);
  }
  private _drawAnalogClock(canvasId: string, timeZone: string) {
  const canvas = document.getElementById(canvasId) as HTMLCanvasElement;
  if (!canvas) return;

  const ctx = canvas.getContext("2d");
  if (!ctx) return;

  const radius = canvas.height / 2;
  ctx.save(); // Save the current drawing state
  ctx.translate(radius, radius);

  const now = new Date();
  const local = new Date(now.toLocaleString("en-US", { timeZone }));
  const hour = local.getHours();
  const minute = local.getMinutes();
  const second = local.getSeconds();

  // Clear previous frame
  ctx.clearRect(-radius, -radius, canvas.width, canvas.height);

  // Draw face with tick marks (no numbers)
  this._drawClockFace(ctx, radius);

  // Draw hour, minute, and second hands
  this._drawHand(ctx, (hour % 12) * 30 + (minute / 60) * 30, radius * 0.5, 4);
  this._drawHand(ctx, minute * 6, radius * 0.75, 2);
  this._drawHand(ctx, second * 6, radius * 0.85, 1, "red");

  ctx.restore(); // Restore original drawing state
}

private _drawClockFace(ctx: CanvasRenderingContext2D, radius: number) {
  // Outer circle
  ctx.beginPath();
  ctx.arc(0, 0, radius - 2, 0, 2 * Math.PI);
  ctx.fillStyle = "#fff";
  ctx.fill();
  ctx.lineWidth = 2;
  ctx.strokeStyle = "#333";
  ctx.stroke();

  // Draw tick marks
  for (let i = 0; i < 60; i++) {
    const angle = (i * Math.PI) / 30; // 6° per tick
    const outer = radius - 5;
    const inner = i % 5 === 0 ? radius - 12 : radius - 8; // longer line for hour marks

    ctx.beginPath();
    ctx.moveTo(inner * Math.sin(angle), -inner * Math.cos(angle));
    ctx.lineTo(outer * Math.sin(angle), -outer * Math.cos(angle));
    ctx.lineWidth = i % 5 === 0 ? 2 : 1; // hour lines thicker
    ctx.strokeStyle = "#000";
    ctx.stroke();
  }

  // Center dot
  ctx.beginPath();
  ctx.arc(0, 0, 2, 0, 2 * Math.PI);
  ctx.fillStyle = "#000";
  ctx.fill();
}
 private _drawHand(ctx: CanvasRenderingContext2D, pos: number, length: number, width: number, color: string = "black") {
    ctx.beginPath();
    ctx.lineWidth = width;
    ctx.lineCap = "round";
    ctx.strokeStyle = color;
    ctx.moveTo(0, 0);
    ctx.rotate((Math.PI / 180) * pos);
    ctx.lineTo(0, -length);
    ctx.stroke();
    ctx.rotate(-(Math.PI / 180) * pos);
  }

  private _onDispose(): void {
    console.log('[AnalogClockApplicationCustomizer] Disposed');
  }
}

Here:

  • private _topPlaceholder: PlaceholderContent | undefined; ->Within the class, we declared this variable to hold the topPlaceholder reference.
  • this.context.placeholderProvider.changedEvent.add(this, this._renderPlaceHolders);
    • this.context.placeholderProvider -> It accesses the placeholder provider object from the SPFx context. So we can manage the available placeholder areas on the SharePoint page (like Top, Bottom).
    • changedEvent ->This is an event triggered when the page changes, for example, the page layout is updated or reloaded.
    • add(this, this._renderPlaceHolders)-> Here, this argument ensures the method runs in the context of your current class instance. Then, this._renderPlaceHolders is a callback function that gets executed whenever that event triggers.
  • _renderPlaceHolders() -> This method checks if the top placeholder is available. Then, it creates custom content inside it. If it doesn’t exist, it displays an error message.
  • this._topPlaceholder.domElement.innerHTM -> This will add a new bar on the top of the SharePoint page. Here we are displaying all the clocks.
  • const cities -> In this array, we list all cities, their time zones, and abbreviations.
  • _createClock() -> This method creates a small canvas element for drawing an analog clock and displays the city name and time zone abbreviation below it. Then it calls _drawAnalogClock() to render the clock. Also, it uses setInterval() to update the clock every second.
  • _drawAnalogClock() -> This method gets the canvas and drawing context, then calculates the local time for the given time zone using toLocalString(). Here, we are calling the _drawClockFace() and _drawHand() helper methods to render the clock face and the hour, minute, and second hands.
  • _drawClockFace() ->With this method, we are making the outer circle for the clock border, the tick marks for minutes and hours, and a small centre dot to complete the design.
  • _drawHand()->This method rotates the canvas to the correct angle based on the time. Also, it draws the hour, minute, and second hands. Then, resets the rotation after each hand is drawn.
  1. Now, we’ll test this SPFx application customizer locally. Open the “serve.json” file in the “config” folder. Then, update the “pageUrl” property in both objects with your SharePoint site page url as shown below.
spfx application customizer analog clock example
  1. Run the “gulp serve” command either in the Terminal pane or the command prompt where you opened this solution. Then it will open a pop-up as shown below. Please click on “Load debug scripts.”
application customizer spfx

Then refresh the SharePoint site once. Immediately, you will be able to see all the clocks we mentioned in the code appear in the SharePoint site’s top placeholder, as shown in the image below.

sharepoint framework extensions application customizer example

This way, we can easily display multiple time zone clocks in the SharePoint site’s top placeholder using the SPFx app customizer extension.

I hope you found this article helpful!, Here, I have explained how to display various time zone clocks in the SharePoint site’s top placeholder using the SPFx application customizer extension. Do let me know if you have any questions.

Also, you may like:

>

Build a High-Performance Project Management Site in SharePoint Online

User registration Power Apps canvas app

DOWNLOAD USER REGISTRATION POWER APPS CANVAS APP

Download a fully functional Power Apps Canvas App (with Power Automate): User Registration App

Power Platform Tutorial FREE PDF Download

FREE Power Platform Tutorial PDF

Download 135 Pages FREE PDF on Microsoft Power Platform Tutorial. Learn Now…