If you’ve ever pulled a date from a SharePoint list in an SPFx web part and ended up staring at something like 2026-05-05T09:30:00Z, you know, the frustration. SharePoint returns dates in UTC ISO 8601 format, and that raw string is never something you’d want to show a user.
In this tutorial, I’ll walk you through every practical way to format dates in SPFx — from simple vanilla JavaScript to using libraries like date-fns and PnPjs. I’ll also cover time zone handling, writing dates back to SharePoint, comparing dates, handling null dates safely, and building a reusable utility file. By the end, you’ll know exactly which approach to use for which situation.
If you want to learn SharePoint Framework development, check out the complete SPFx training course.
Why Date Formatting in SPFx Is Trickier Than It Looks
Before we get into the methods, let me quickly explain why date handling in SPFx isn’t as simple as it sounds.
When you query a SharePoint list using the REST API or PnPjs, date columns come back in UTC format, like this:
2026-05-05T09:30:00Z
The problem is that the user sitting in Bengaluru or Berlin doesn’t want to see that. They want to see something like May 05, 2026 or 05/05/2026 — ideally in their own time zone.
So formatting a date in SPFx really means two things:
- Parsing the UTC string correctly
- Displaying it in a human-friendly format (with or without time zone conversion)
There are also a few gotchas developers run into constantly:
getMonth()is zero-indexed in JavaScript — November is10, not11new Date(undefined)silently returnsInvalid Dateand can crash your component- Saving a date without specifying the time can roll it back a day for UTC+ users
- Moment.js adds massive bundle size and is now in maintenance mode — avoid it for new SPFx projects
Let’s get into the methods.
Method 1: Using JavaScript’s Built-in Date Object
This is the simplest approach and requires zero extra libraries. If you just need basic formatting, the native JavaScript Date object plus toLocaleDateString() does the job nicely.
Basic example
Say you get this date back from SharePoint:
const rawDate = "2026-11-15T09:30:00Z";
const date = new Date(rawDate);
const formatted = date.toLocaleDateString("en-US");
console.log(formatted); // 11/15/2026
That gives you a simple US-format date. But toLocaleDateString() is way more powerful than that. You can pass an options object to control exactly how it looks.
Custom formats with Intl.DateTimeFormatOptions
const rawDate = "2026-11-15T09:30:00Z";
const date = new Date(rawDate);
const options: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "long",
day: "2-digit"
};
const formatted = date.toLocaleDateString("en-US", options);
console.log(formatted); // November 15, 2026
You can swap "long" for "short" or "numeric" for the month:
month value | Output |
|---|---|
"long" | November |
"short" | Nov |
"numeric" | 11 |
"2-digit" | 11 |
Formatting date AND time together
const options: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "short",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: true
};
const formatted = date.toLocaleString("en-US", options);
console.log(formatted); // Nov 15, 2026, 03:00 PM

When to use this
- You want zero extra dependencies in your project
- You just need basic locale-aware formatting
- Bundle size matters to you (and it should, in SPFx)
Downside: Time zone conversion requires extra work with this approach.
Check out SharePoint Framework (SPFx) Form Validation
Method 2: Write Your Own Format Function
Sometimes you need full control — maybe you want DD-MM-YYYY and nothing else. The cleanest way is to write a small utility function.
function formatDate(dateString: string): string {
const date = new Date(dateString);
const day = date.getDate().toString().padStart(2, "0");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const year = date.getFullYear();
return `${day}-${month}-${year}`;
}
console.log(formatDate("2026-11-15T09:30:00Z")); // 15-11-2026
A couple of things to note here:
getMonth()is zero-indexed in JavaScript, so November is10, not11. That’s why I always add+1.padStart(2, "0")ensures you get05instead of5.
Adding time to the output
function formatDateTime(dateString: string): string {
const date = new Date(dateString);
const day = date.getDate().toString().padStart(2, "0");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const year = date.getFullYear();
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return `${day}-${month}-${year} ${hours}:${minutes}`;
}
console.log(formatDateTime("2026-11-15T09:30:00Z")); // 15-11-2026 09:30

When to use this
- You need a very specific output format
- You’re building a reusable utility file for your SPFx project
- You want no library overhead whatsoever
Read SPFx Web Part Audience Targeting
Method 3: Using the date-fns Library
If your project handles a lot of date manipulation — not just formatting — I’d recommend date-fns over moment.js. It’s modular, so you only import what you actually use, which keeps the bundle size down. Moment.js, in contrast, is quite heavy and is now in maintenance mode.
Install it first
npm install date-fns --save
Basic formatting
import { format } from "date-fns";
const dateFnsDate = new Date("2026-11-15T09:30:00Z");
const dateFnsFormatted = format(dateFnsDate, "dd-MM-yyyy");
console.log(dateFnsFormatted); // 15-11-2026
Common format tokens in date-fns
| Token | Meaning | Example |
|---|---|---|
dd | Day with leading zero | 05 |
d | Day without leading zero | 5 |
MM | Month number with leading zero | 11 |
MMMM | Full month name | November |
MMM | Short month name | Nov |
yyyy | 4-digit year | 2024 |
HH | 24-hour time | 09 |
hh | 12-hour time | 09 |
mm | Minutes | 30 |
a | AM/PM | AM |
Formatting with full date and time
import { format } from "date-fns";
const dateFnsDateTime = format(new Date("2026-11-15T09:30:00Z"), "MMMM dd, yyyy 'at' hh:mm a");
console.log(dateFnsDateTime); // November 15, 2026 at 03:00 PM

Date Math with date-fns
One major advantage of date-fns is the ability to do date arithmetic cleanly:
import { addDays, subMonths, differenceInDays, isPast, isAfter } from "date-fns";
const today = new Date();
const dueDate = new Date("2026-12-31T00:00:00Z");
// Add 7 days to today
const nextWeek = addDays(today, 7);
// Subtract 1 month
const lastMonth = subMonths(today, 1);
// Difference between two dates
const daysUntilDue = differenceInDays(dueDate, today);
console.log(`Days until due: ${daysUntilDue}`);
// Check if a date is in the past
const isOverdue = isPast(dueDate);
console.log(`Is overdue: ${isOverdue}`);
// Check if one date is after another
console.log(isAfter(dueDate, today)); // true or false
This is especially useful in SPFx task management web parts where you need to highlight overdue items or calculate SLA deadlines.
When to use date-fns
- You need to do date math (add days, subtract months, compare dates, check if a date is past)
- You want clean, tree-shakable imports
- You don’t want to deal with moment.js’s legacy baggage
Check out SPFx Environment Variables
Method 4: Handling Time Zones Correctly
This is where most developers get tripped up. When SharePoint returns a date like 2026-11-15T09:30:00Z, the Z means UTC. If your user is in India (UTC+5:30), that’s actually 3:00 PM IST — not 9:30 AM.
There are two strategies here.
Strategy A: Use FieldValuesAsText with PnPjs
This is the simplest approach and my personal favorite for most cases. Instead of converting time zones manually, you let SharePoint do it for you by requesting FieldValuesAsText. SharePoint returns the date already formatted in the site’s regional settings time zone.
import { spfi, SPFx } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
const sp = spfi().using(SPFx(this.context));
const items = await sp.web.lists
.getByTitle("Events")
.items
.select("Title", "EventDate", "FieldValuesAsText/EventDate")
.expand("FieldValuesAsText")();
items.forEach(item => {
// This date is already formatted per SharePoint's regional settings
const displayDate = item.FieldValuesAsText.EventDate;
console.log(displayDate); // e.g., "11/15/2026 9:30 AM"
});
No conversion, no library — SharePoint does the heavy lifting.
Strategy B: Convert UTC to a specific time zone using Intl
If you need more control, you can use the built-in Intl.DateTimeFormat with a time zone option. No extra libraries needed.
const tzDate = new Date("2026-11-15T09:30:00Z");
const formatted4 = new Intl.DateTimeFormat("en-IN", {
timeZone: "Asia/Kolkata",
year: "numeric",
month: "long",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: true
}).format(tzDate);
console.log(formatted4);
You can swap "Asia/Kolkata" with any IANA time zone string like "America/New_York" or "Europe/London".

Getting the user’s site time zone dynamically
If you need to match SharePoint’s site time zone dynamically, you can read it from the page context:
const legacyCtx: any = this.context.pageContext.legacyPageContext;
const siteTimezone = legacyCtx.webTimeZoneData?.Description;
// Use this to map to IANA format and pass to date-fns-tz or Intl
Check out Migrate from Gulp based to Heft based Toolchain in SharePoint Framework (SPFx)
Method 5: Comparing Dates in SPFx
Comparing two SharePoint dates is a common requirement — especially for highlighting overdue tasks, filtering by date range, or sorting list items.
Simple Date Comparison
const date1 = new Date("2026-11-15T09:30:00Z");
const date2 = new Date("2026-12-01T09:30:00Z");
if (date1 < date2) {
console.log("date1 is earlier");
} else if (date1 > date2) {
console.log("date1 is later");
} else {
console.log("Dates are equal");
}
Check If a SharePoint Date Has Passed
function isDatePast(dateString: string | null): boolean {
if (!dateString) return false;
return new Date(dateString) < new Date();
}
// Usage in React component
const overdue = isDatePast(item.DueDate);
Sort SharePoint List Items by Date
const sortedItems = items.sort((a, b) => {
return new Date(a.Created).getTime() - new Date(b.Created).getTime();
});
This pattern is useful when you’re fetching items from multiple lists and need to merge and sort them by date on the client side.
Method 6: Handling Null and Invalid Dates Safely
SharePoint date fields can be empty, and new Date(undefined) or new Date(null) will return Invalid Date, which silently breaks your component. Always guard against this.
Safe Format Wrapper
function safeFormatDate(dateString: string | null | undefined): string {
if (!dateString) return "—";
const date = new Date(dateString);
if (isNaN(date.getTime())) return "Invalid date";
return date.toLocaleDateString("en-GB", {
day: "2-digit",
month: "short",
year: "numeric"
});
}
console.log(safeFormatDate(null)); // —
console.log(safeFormatDate("not-a-date")); // Invalid date
console.log(safeFormatDate("2026-11-15T09:30:00Z")); // 15 Nov 2026

Always use a wrapper like this when rendering dates from SharePoint list items in your React components.
Method 7: Formatting Dates for Writing Back to SharePoint
This one catches people out. When you’re reading dates, you have flexibility. But when you’re writing a date back to a SharePoint list, SharePoint expects a very specific UTC format: YYYY-MM-DDTHH:mm:ssZ.
Here’s a simple utility function for that:
function toSharePointDate(date: Date): string {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
return `${year}-${month}-${day}T00:00:00Z`;
}
// Example: saving a date using PnPjs
const sp = spfi().using(SPFx(this.context));
await sp.web.lists.getByTitle("Tasks").items.add({
Title: "New Task",
DueDate: toSharePointDate(new Date("2026-12-31"))
});
A common gotcha here: if you’re working with a date-only field (not date+time), make sure you set the time to T00:00:00Z so the UTC conversion doesn’t accidentally roll it back to the previous day for users in UTC+ time zones.

Method 8: Building a Reusable Date Utility File
The same methods work in SPFx extensions and across multiple web parts, but copy-pasting code everywhere is messy. The clean pattern is to centralize all your date logic in one reusable utility file.
Create a file called dateUtils.ts in your project’s utils folder:
// src/utils/dateUtils.ts
export function formatToDisplay(dateString: string | null | undefined): string {
if (!dateString) return "—";
const date = new Date(dateString);
if (isNaN(date.getTime())) return "Invalid date";
return date.toLocaleDateString("en-GB", {
day: "2-digit",
month: "short",
year: "numeric"
});
}
export function formatToDisplayWithTime(dateString: string | null | undefined): string {
if (!dateString) return "—";
const date = new Date(dateString);
if (isNaN(date.getTime())) return "Invalid date";
return date.toLocaleString("en-GB", {
day: "2-digit",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: true
});
}
export function formatToSharePoint(date: Date): string {
const y = date.getFullYear();
const m = (date.getMonth() + 1).toString().padStart(2, "0");
const d = date.getDate().toString().padStart(2, "0");
return `${y}-${m}-${d}T00:00:00Z`;
}
export function isDatePast(dateString: string | null | undefined): boolean {
if (!dateString) return false;
return new Date(dateString) < new Date();
}
export function daysBetween(dateString1: string, dateString2: string): number {
const d1 = new Date(dateString1);
const d2 = new Date(dateString2);
const diffMs = Math.abs(d2.getTime() - d1.getTime());
return Math.floor(diffMs / (1000 * 60 * 60 * 24));
}

Then import wherever you need it:
import { formatToDisplay, isDatePast, daysBetween } from "../../utils/dateUtils";
const displayDate = formatToDisplay(item.Created); // 15 Nov 2026
const overdue = isDatePast(item.DueDate); // true or false
const age = daysBetween(item.Created, item.Modified); // number of days

This keeps your components clean and your date logic centralized, tested, and easy to maintain across the entire project.
Method 9: Formatting Dates in SPFx Extensions (ListView Command Sets / Application Customizers)
The same methods work in extensions, but you won’t have a component state to work with directly. A clean pattern is to export a reusable date utility file.
Create a file called dateUtils.ts in your project’s utils folder:
// src/utils/dateUtils.ts
export function formatToDisplay(dateString: string | null): string {
if (!dateString) return "—";
const date = new Date(dateString);
return date.toLocaleDateString("en-GB", {
day: "2-digit",
month: "short",
year: "numeric"
});
}
export function formatToSharePoint(date: Date): string {
const y = date.getFullYear();
const m = (date.getMonth() + 1).toString().padStart(2, "0");
const d = date.getDate().toString().padStart(2, "0");
return `${y}-${m}-${d}T00:00:00Z`;
}
Then import wherever you need it:
import { formatToDisplay } from "../../utils/dateUtils";
const displayDate = formatToDisplay(item.Created); // 15 Nov 2026
This keeps your components clean and your date logic centralized.
Which Method Should You Use?
| Scenario | Best Approach |
|---|---|
| Simple display with locale awareness | toLocaleDateString() with options |
| Specific fixed format (DD-MM-YYYY) | Custom utility function |
| Lots of date math + formatting | date-fns |
| Respect SharePoint’s regional settings | FieldValuesAsText with PnPjs |
| Time zone-aware display | Intl.DateTimeFormat with timeZone |
| Writing a date back to SharePoint | Custom toSharePointDate() function |
| Null-safe display in React components | Safe wrapper with isNaN check |
| Shared logic across web parts/extensions | Centralized dateUtils.ts utility file |
Things to Keep in Mind
- Never use moment.js for new SPFx projects. It’s in maintenance mode, adds significant bundle size, and
date-fnsor nativeIntlhandles everything it does. - Always check for null/undefined before formatting. SharePoint date fields can be empty, and
new Date(undefined)returnsInvalid Date, which crashes things silently. - The UTC shift trap is real. If a user picks November 15 in a date picker and you save it as
new Date("2024-11-15")without handling the time zone, it may show up as November 14 in some environments. Always set time explicitly when writing to SharePoint. date-fnsis tree-shakable, meaning you only pay the bundle cost for the functions you actually import — unlike moment.js, which ships the whole thing regardless.- Use
FieldValuesAsTextwhen you simply want to display dates as SharePoint already formats them per the site’s regional settings — it’s the lowest-effort, most reliable approach for read-only display scenarios. - Centralize date logic in
dateUtils.tsonce your project grows beyond two or three web parts. It makes maintenance and testing significantly easier.
Wrapping Up
In this guide, we covered eight practical ways to format and work with dates in SPFx — from the built-in JavaScript Date object and custom utility functions, to date-fns for date math, time zone handling with Intl.DateTimeFormat and FieldValuesAsText, comparing and sorting dates, handling null values safely, and formatting dates correctly when writing back to SharePoint.
You don’t need all of them in every project — just pick the approach that fits your situation best, and consider building a shared dateUtils.ts file as your project grows.
Try them out and let me know in the comments which method works best for you!
Also, you may like:
- Build a Custom Slides Manager in SPFx Web Part Property Pane (Drag & Drop, Reorder, Hide Slides)
- Build a SharePoint Folder Tree View Using SharePoint Framework (SPFx)
- SPFx ListView Command Set Extension Tutorial: Create & Deploy with Example
- How to Add Custom Controls in SPFx Property Pane

Hey! I’m Bijay Kumar, founder of SPGuides.com and a Microsoft Business Applications MVP (Power Automate, Power Apps). I launched this site in 2020 because I truly enjoy working with SharePoint, Power Platform, and SharePoint Framework (SPFx), and wanted to share that passion through step-by-step tutorials, guides, and training videos. My mission is to help you learn these technologies so you can utilize SharePoint, enhance productivity, and potentially build business solutions along the way.