DEV Community

Cover image for 7 npm packages you can delete now that the browser does it natively
kshitij Bhatnagar
kshitij Bhatnagar

Posted on

7 npm packages you can delete now that the browser does it natively

Open your package.json and look at the small stuff. Not React or your
framework, the little helpers: a deep clone here, a UUID there, a date library
you pulled in to print "3 hours ago."

A lot of those are now built into the browser (and modern Node). The platform
quietly grew up while we kept installing the same habits. Here are seven you can
probably delete today, with the before and after.

1. Deep clone: structuredClone()

The old reflex is JSON.parse(JSON.stringify(obj)), or reaching for
lodash.cloneDeep. The JSON trick silently corrupts your data: it turns Date
objects into strings, drops undefined, and throws on anything cyclic.

// before
import cloneDeep from 'lodash.clonedeep';
const copy = cloneDeep(state);

// after, built in
const copy = structuredClone(state);
Enter fullscreen mode Exit fullscreen mode

structuredClone handles Date, Map, Set, typed arrays, and cyclic
references correctly. It cannot clone functions or DOM nodes, which is usually
what you want anyway.

2. UUIDs: crypto.randomUUID()

The uuid package is one of the most downloaded on npm. For a v4 UUID you no
longer need it.

// before
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4();

// after, built in
const id = crypto.randomUUID();
Enter fullscreen mode Exit fullscreen mode

One gotcha: crypto.randomUUID() only runs in a secure context (HTTPS or
localhost). In Node it lives on the same global from node:crypto.

3. Dates, numbers, and "time ago": Intl

Most of the time we import moment or date-fns just to format something. Intl
does formatting natively, localized, with zero dependencies.

// currency, localized
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(1299.5);
// "$1,299.50"

// "3 hours ago" without a library
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
rtf.format(-3, 'hour'); // "3 hours ago"

// readable dates
new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium' }).format(new Date());
Enter fullscreen mode Exit fullscreen mode

If you are doing heavy date math (adding months, parsing weird formats), a
library still earns its place, and Temporal is on the way. But for display,
Intl is almost always enough.

4. HTTP requests: fetch (with a real timeout)

fetch is everywhere now, including Node 18 and up. The two reasons people kept
axios were timeouts and error handling, and both have native answers.

// before
import axios from 'axios';
const { data } = await axios.get(url, { timeout: 5000 });

// after, built in
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
Enter fullscreen mode Exit fullscreen mode

The one thing to remember: fetch does not reject on 404 or 500, so check
res.ok yourself. If you lean on interceptors, axios is still a fair choice.

5. Modals: the <dialog> element

Accessible modals used to mean a library or a pile of ARIA and focus-trapping
code. The <dialog> element gives you the backdrop, focus trapping, and
Escape to close for free.

<dialog id="confirm">
  <form method="dialog">
    <p>Delete this file?</p>
    <button value="cancel">Cancel</button>
    <button value="ok">Delete</button>
  </form>
</dialog>
Enter fullscreen mode Exit fullscreen mode
document.getElementById('confirm').showModal();
Enter fullscreen mode Exit fullscreen mode

showModal() traps focus and renders the ::backdrop. Submitting the form
closes the dialog and hands you the button's value.

6. Styling a parent: CSS :has()

We used to add JavaScript just to toggle a class on a parent when something
inside changed. :has() is the parent selector we asked for over a decade.

/* highlight a card that contains a checked box */
.card:has(input:checked) { outline: 2px solid green; }

/* style a form that has an invalid field */
form:has(:invalid) .submit { opacity: 0.5; }
Enter fullscreen mode Exit fullscreen mode

That is real logic, in CSS, with no event listeners to wire up or tear down.

7. Tooltips and menus: the Popover API

Popovers meant z-index wars and outside-click handlers. The Popover API puts the
element in the top layer, gives you light dismiss (click outside or Escape), and
needs no JavaScript for the basic case.

<button popovertarget="menu">Options</button>
<div id="menu" popover>
  <button>Rename</button>
  <button>Delete</button>
</div>
Enter fullscreen mode Exit fullscreen mode

This is the newest one on the list, so check support for your audience and treat
it as progressive enhancement. The other six are broadly safe in modern
browsers today.

How to know it is safe to delete

Before you rip anything out: search the feature on caniuse.com
or check whether it is Baseline for your target
browsers. For a library you already ship in production, there is no prize for
removing it in a hurry. The real win is for new code: reach for the platform
first, and only add a dependency when the platform genuinely falls short.

Your node_modules gets lighter, your bundle gets smaller, and you own less
supply chain risk for free.

Which of these did you not know was native yet? And what would you add as number
eight?

Top comments (0)