
6 Native Web APIs That Finally Replace Your Most-Used NPM Libraries
Stop bloating your bundle and start leveraging the high-performance features like Temporal and Popover that are now built into your users' browsers.
We’ve spent the last decade treating the browser like a dumb terminal, shipping massive JavaScript bundles to handle basic tasks like "showing a popup" or "parsing a date." It’s a bit ridiculous that we optimize our images to the last byte but then casually drop a 2MB node_modules folder into our user’s browser just to format a string.
The "New Browser" isn’t just a window for your framework to live in; it’s a powerhouse. Here are six native APIs that allow you to finally hit npm uninstall on some of your heaviest dependencies.
1. The Popover API
Until recently, building a simple tooltip or dropdown menu meant fighting a losing war against z-index and overflow: hidden. You usually ended up reaching for a library like Floating UI or some heavy React wrapper just to ensure your menu didn't get clipped by a parent container.
The native Popover API solves this by moving elements to the "top layer"—a magical place in the browser that sits above everything else, regardless of CSS nesting.
<button popovertarget="my-menu">Open Settings</button>
<div id="my-menu" popover>
<p>Look, Ma! No external libraries.</p>
<button popovertarget="my-menu" popovertargetaction="hide">Close</button>
</div>Why it wins: It handles "light dismiss" (clicking outside to close) and keyboard navigation (ESC key) for free. No state management required.
2. Temporal (The Moment.js Killer)
The standard Date object in JavaScript is, to put it politely, a disaster. It’s mutable, it handles months starting at 0, and parsing timezones is enough to make a grown developer cry. We’ve been leaning on moment.js or dayjs for years to bridge the gap.
Temporal is the new global object (currently in Stage 3 and landing in browsers) that fixes this. It’s immutable and makes timezone math actually legible.
// No more mystery math. This is readable.
const today = Temporal.Now.plainDateISO();
const nextWeek = today.add({ days: 7 });
const duration = Temporal.Duration.from({ hours: 2, minutes: 30 });
const meetingStart = Temporal.ZonedDateTime.from('2024-10-01T10:00[America/New_York]');
console.log(meetingStart.toString());The Gotcha: While it’s the future, you might still need a tiny polyfill for older browsers today, but the API is vastly superior to the legacy Date object.
3. The <dialog> Element
Modals are surprisingly hard to get right. You need to trap focus, manage ARIA roles, and prevent scrolling on the body. Usually, we just install a "Modal" component and call it a day.
The native <dialog> element handles almost all of this with zero configuration.
const modal = document.querySelector('#settings-modal');
// Opens as a true modal (traps focus, adds backdrop)
modal.showModal();
// Simple close method
modal.close();The Pro Tip: Use the ::backdrop pseudo-element to style that dim background. It’s much cleaner than creating a separate div for the overlay.
4. structuredClone()
How many times have you seen const copy = JSON.parse(JSON.stringify(obj))? It’s a hacky way to deep-clone an object, and it fails miserably if you have Dates, Sets, Maps, or circular references. Many developers pull in lodash.cloneDeep just to avoid this.
Enter structuredClone(). It’s built-in, it’s fast, and it works exactly how you expect.
const original = {
name: "Original",
date: new Date(),
metadata: new Map([['id', 123]])
};
const copy = structuredClone(original);
console.log(copy.date instanceof Date); // true
console.log(copy === original); // falseCaveat: It still won't clone functions or DOM elements, which makes sense—how do you "clone" a live reference to a click handler anyway?
5. Intl (Internationalization API)
If you are shipping date-fns or numeral.js just to format currency or show "2 days ago," you’re wasting your users' bandwidth. The Intl object is massive and covers almost every formatting need.
Need relative time?
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
console.log(rtf.format(-1, 'day')); // "yesterday"
console.log(rtf.format(2, 'week')); // "in 2 weeks"Need currency?
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
});
console.log(formatter.format(2500.50)); // "$2,500.50"The browser already knows the locale data for every language. Why ship it again in a library?
6. URLPattern
Routing is the heart of most web apps. Whether you’re building a custom router or just trying to extract an ID from a URL, we usually end up writing messy Regex or importing a library like path-to-regexp.
The URLPattern API lets you match URLs against a template string using a syntax very similar to Express or React Router.
const pattern = new URLPattern({ pathname: '/posts/:id' });
const match = pattern.exec(window.location.href);
if (match) {
console.log(match.pathname.groups.id); // "123"
}It’s currently available in Chromium browsers and coming to others. If you’re building Edge Functions or Service Workers, this is a lifesaver.
Wrap up
The "JavaScript Fatigue" we all complain about often comes from the overhead of managing dependencies that the platform now provides for free. Next time you're about to npm install a utility, take five minutes to check MDN. You might find the browser has already solved your problem while you were sleeping.


