Temporal Is In ES2026 and You Still Cannot Ship It Without a Polyfill
Temporal hit Stage 4 in March 2026 and shipped in Chrome, Edge and Firefox. Safari has none of it. What that means for using it today.

Table of contents
The Temporal API reached TC39 Stage 4 in March 2026, which makes it part of the ES2026 specification and the official replacement for Date. A wave of posts announced that the nine-year wait was over.
Then you check Safari. It is not in any shipped version — only Technical Preview. Global support sits at 69.2%, and MDN classifies the API as Limited Availability rather than Baseline.
Stage 4 means the specification is final. It does not mean you can use it.
Key takeaways#
- Stage 4 is a spec milestone, not a support milestone. They are frequently years apart.
- Chrome 144, Edge 144, Firefox 139 and Opera 131 ship it. Safari ships none of it.
- That puts global support at about 69% — roughly one visitor in three has no
Temporalobject. - The official polyfill is around 40 kB gzipped, which is a real cost for a date library.
- The parts worth adopting first are the ones that fix bugs
Dateactively causes.
What does Stage 4 actually guarantee?#
That the proposal is finished, has test coverage in test262, has two independent implementations, and will appear in the next edition of the language. It is the end of the standardisation process.
Browser shipping is a separate track with its own timelines. WebKit implemented it behind a flag and it remains in Technical Preview, which is not a version anyone browses with. Until it lands in a stable Safari release and users update, a third of the web has no access.
The gap between the two is normal and routinely misread. Optional chaining was Stage 4 in 2019 and safe to use unpolyfilled around 2022. Treat Stage 4 as "start learning it", not "start shipping it".
What is actually broken about Date?#
Worth restating, because it is why Temporal matters at all rather than being a nicer syntax.
Date is mutable, so passing one to a function that calls setMonth changes the caller's value. It has exactly one time zone concept, the host's, so representing "9am in Tokyo" requires either a string or a library. Month indexes start at zero while day-of-month starts at one. And parsing non-ISO strings is implementation-defined, which is why new Date('2026-09-02') and new Date('09/02/2026') can produce different days depending on the browser.
The new API fixes each of these deliberately. Objects are immutable, so arithmetic returns a new value. Types are explicit about what they represent:
| Type | Represents |
|---|---|
Temporal.Instant | An exact point in time, nanosecond precision |
Temporal.ZonedDateTime | An instant plus a real time zone |
Temporal.PlainDate | A calendar date with no time and no zone |
Temporal.PlainTime | A wall-clock time with no date |
Temporal.Duration | A length of time |
That table is the whole design argument. Most date bugs come from one value being used as though it were a different one of those five things, and Date cannot tell them apart.
Can I use it today?#
Three options, in the order most teams should consider them.
Polyfill it. @js-temporal/polyfill gives you the whole surface now. The cost is bundle size — roughly 40 kB gzipped, which is more than date-fns for many use cases and comparable to the Luxon you might already be shipping. If you are replacing a date library rather than adding one, the trade is fine. If you are adding it to a codebase that had no date library, you are paying 40 kB for ergonomics.
Feature-detect and progressively enhance. Reasonable when it is used for something non-essential such as a nicer relative-time display.
if (typeof Temporal !== 'undefined') {
// native path
} else {
// Date fallback
}The catch is that you now maintain two code paths for the same logic, which is usually worse than either alternative.
Wait. Genuinely the right answer for a lot of projects. Nothing breaks by continuing to use Date or an existing library for another year, and the polyfill cost disappears entirely once Safari ships.
What should I adopt first when it is available?#
The parts that remove a class of bug rather than the parts that read nicely.
Start with ZonedDateTime anywhere you currently store a local wall-clock time and hope the server agrees. That single substitution eliminates most daylight-saving bugs, because the zone travels with the value instead of being applied at render.
Then PlainDate for anything that is genuinely a date rather than an instant — a birthday, an invoice date, a public holiday. Storing those as a Date forces a time and a zone onto a value that has neither, which is why a birthday sometimes displays as the day before.
Duration last. It is the most pleasant part of the API and the least likely to be silently wrong today.
One thing it does not change: a Unix timestamp is still an integer with no unit attached. Temporal.Instant.fromEpochMilliseconds and fromEpochSeconds are separate methods for the same reason the bug exists in the first place. If you are staring at a raw number, the timestamp converter shows both readings at once.
The short version#
It is finished, correct, and not yet shippable to everyone. Chrome, Edge and Firefox have it; Safari has none of it, which caps real-world support near 69%. Learn the five types now, because they will change how you model dates. Reach for the polyfill only if you are replacing a library you already ship, and otherwise let Safari catch up.
Sources: MDN: Temporal, Can I use: Temporal
Tags
- JavaScript
- Temporal
- Dates
- Browser Support