You're probably writing more code than needed.
Modern JavaScript already solved most of these problems.
🚀 The Problem (Real Talk)
In many production codebases, I still see:
-
lodashfor simple utilities - JSON hacks for deep cloning
- manual formatting logic
- mutation-heavy patterns
👉 This leads to:
- bigger bundle sizes
- harder debugging
- subtle bugs (especially in React apps)
👉 Modern JavaScript already has built-in solutions for most of these problems.
🎯 What You'll Learn
In this post:
- Replace outdated patterns
- Write cleaner, safer code
- Use modern APIs already available in JavaScript
🧩 Modern APIs You Should Already Be Using
1. Deep Copy Objects (Without Breaking Data)
❌ Old way
const copy = JSON.parse(JSON.stringify(obj));
⚠️ Why this is dangerous
- Breaks
Date,Map,Set - Removes
undefined - Causes hidden bugs
const obj = {
date: new Date(),
role: undefined,
scores: new Map([["math", 95]])
};
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy.date instanceof Date); // false ❌ — becomes a string
console.log(copy.role); // gone ❌ — undefined is stripped
console.log(copy.scores instanceof Map); // false ❌ — becomes {}
✅ Modern way
const copy = structuredClone(obj);
console.log(copy.date instanceof Date); // true ✅
console.log(copy.role); // undefined ✅
console.log(copy.scores instanceof Map); // true ✅
🧠 Real-world use case
// Safely editing form state in React without mutating original
const handleEdit = (user) => {
const draft = structuredClone(user); // deep copy
draft.address.city = "Hyderabad"; // safe — original untouched
setEditingUser(draft);
};
✅ Works with:
Date,Map,Set,ArrayBuffer, nested objects
❌ Does NOT clone: functions, DOM nodes, class instances
2. Group Data Without Reduce Hell
❌ Old way
const grouped = users.reduce((acc, user) => {
const key = user.role;
acc[key] = acc[key] || [];
acc[key].push(user);
return acc;
}, {});
✅ Modern way
const grouped = Object.groupBy(users, user => user.role);
🧠 Real-world use cases
const orders = [
{ id: 1, status: "pending", item: "Book" },
{ id: 2, status: "delivered", item: "Pen" },
{ id: 3, status: "pending", item: "Laptop" },
{ id: 4, status: "cancelled", item: "Bag" },
];
const byStatus = Object.groupBy(orders, order => order.status);
console.log(byStatus.pending);
// [{ id: 1, item: "Book" }, { id: 3, item: "Laptop" }]
// Group by price range
const products = [
{ name: "Pen", price: 20 },
{ name: "Book", price: 250 },
{ name: "Laptop", price: 55000 },
];
const byRange = Object.groupBy(products, p =>
p.price < 100 ? "cheap" : p.price < 1000 ? "mid" : "expensive"
);
console.log(byRange.cheap); // [{ name: "Pen", price: 20 }]
console.log(byRange.expensive); // [{ name: "Laptop", price: 55000 }]
🔁 Also available as
Map.groupBy(arr, fn)if you need a Map instead of a plain object
3. Access Last Element
❌ Old way
const last = arr[arr.length - 1]; // verbose
const last = arr.slice(-1)[0]; // creates a new array just for one item
✅ Modern way
const last = arr.at(-1);
🧠 Real-world use cases
const chatHistory = [
{ role: "user", text: "Hi" },
{ role: "assistant", text: "Hello! How can I help?" },
{ role: "user", text: "What is JavaScript?" },
];
const lastMessage = chatHistory.at(-1);
console.log(lastMessage.text); // "What is JavaScript?"
// Works on strings too
const filename = "profile-photo-final-v3.png";
const extension = filename.split(".").at(-1);
console.log(extension); // "png"
// Negative indexing
console.log(arr.at(-1)); // last
console.log(arr.at(-2)); // second to last
console.log(arr.at(-3)); // third to last
4. Cancel API Calls
❌ Old way — no real cancellation
// isCancelled flag doesn't stop the network request
let isCancelled = false;
fetch("/api/search?q=hello")
.then(res => res.json())
.then(data => {
if (!isCancelled) updateUI(data); // request still happened ❌
});
isCancelled = true; // too late, request already in flight
✅ Modern way
const controller = new AbortController();
fetch("/api/data", { signal: controller.signal });
// Actually cancels the network request
controller.abort();
🧠 Real-world use case — Search input
let controller;
searchInput.addEventListener("input", async (e) => {
// Cancel previous request if user is still typing
if (controller) controller.abort();
controller = new AbortController();
try {
const res = await fetch(`/api/search?q=${e.target.value}`, {
signal: controller.signal
});
const data = await res.json();
renderResults(data);
} catch (err) {
if (err.name === "AbortError") return; // ignore cancelled requests
console.error("Search failed:", err);
}
});
// React useEffect cleanup — cancel fetch on unmount
useEffect(() => {
const controller = new AbortController();
fetch("/api/user/profile", { signal: controller.signal })
.then(res => res.json())
.then(setUser)
.catch(err => {
if (err.name !== "AbortError") console.error(err);
});
return () => controller.abort(); // ✅ clean cancel on unmount
}, []);
5. Copy to Clipboard
❌ Old way
// Deprecated — execCommand is removed in many browsers
const el = document.createElement("textarea");
el.value = text;
document.body.appendChild(el);
el.select();
document.execCommand("copy"); // ❌ deprecated
document.body.removeChild(el);
✅ Modern way
await navigator.clipboard.writeText("Hello World");
🧠 Real-world use case
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showToast("✅ Copied!");
} catch (err) {
showToast("❌ Failed to copy");
}
}
// Copy code block on button click
copyBtn.addEventListener("click", () => {
copyToClipboard(codeBlock.innerText);
});
// Read from clipboard
async function pasteFromClipboard() {
const text = await navigator.clipboard.readText();
inputField.value = text;
}
⚠️ Requires HTTPS or localhost
⚠️ User must grant clipboard permission forreadText()
6. Style Parent Elements (CSS :has())
❌ Old way — required JavaScript
// Had to use JS to style parents based on children
document.querySelectorAll(".card").forEach(card => {
if (card.querySelector("img")) {
card.classList.add("has-image"); // ❌ manual JS
}
});
✅ Modern way — pure CSS
.card:has(img) {
border: 2px solid green;
}
🧠 Real-world use cases
/* Style form group when input is focused */
.form-group:has(input:focus) {
background: #f0f9ff;
border-color: #3b82f6;
}
/* Highlight option row when its checkbox is checked */
.option:has(input[type="checkbox"]:checked) {
background: #dcfce7;
font-weight: bold;
}
/* Error card styling */
.card:has(.error-message) {
border: 1px solid red;
background: #fff5f5;
}
/* Nav item active state */
.nav-item:has(a.active) {
background: var(--primary);
border-radius: 8px;
}
/* Adjust layout when sidebar has an ad */
.sidebar:has(.ad-banner) ~ .content {
margin-right: 260px;
}
✅ Supported in all modern browsers (Chrome 105+, Safari 15.4+, Firefox 121+)
🔥 Most powerful CSS selector added in years — eliminates entire JS workaround patterns
7. First Successful Promise
❌ Old way — Promise.race() rejects on first failure
// If the fastest promise fails, the whole thing rejects ❌
const result = await Promise.race([
fetch("/cdn1/data"),
fetch("/cdn2/data"),
]);
✅ Modern way
// Returns first SUCCESS — ignores failures ✅
const result = await Promise.any([
fetch("/fast"),
fetch("/slow")
]);
🧠 Real-world use cases
// Try multiple CDNs — use whichever responds successfully first
const image = await Promise.any([
fetch("https://cdn1.example.com/logo.png"),
fetch("https://cdn2.example.com/logo.png"),
fetch("https://cdn3.example.com/logo.png"),
]);
// Try multiple API regions for lowest latency
const data = await Promise.any([
fetch("https://api-us.example.com/users"),
fetch("https://api-eu.example.com/users"),
fetch("https://api-ap.example.com/users"),
]).then(res => res.json());
// Handle when ALL fail
try {
const result = await Promise.any([
fetch("/primary"),
fetch("/fallback"),
]);
} catch (err) {
if (err instanceof AggregateError) {
console.log("All sources failed:", err.errors);
}
}
📊 Promise method cheat sheet
| Method | Resolves when | Rejects when |
|---|---|---|
Promise.all() |
ALL succeed | ANY fails |
Promise.race() |
FIRST settles | FIRST settles as failure |
Promise.allSettled() |
ALL settle | Never |
Promise.any() |
FIRST succeeds | ALL fail |
8. Format Currency (and Numbers, Dates, Relative Time)
❌ Old way — fragile string building
// Not locale-aware, breaks for ₹, €, ¥
function formatCurrency(amount) {
return "$" + amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
✅ Modern way
new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
}).format(1000);
// $1,000.00
🧠 Real-world use cases
// Multiple currencies — locale-aware automatically
formatCurrency(1000, "en-US", "USD"); // $1,000.00
formatCurrency(1000, "en-IN", "INR"); // ₹1,000.00
formatCurrency(1000, "de-DE", "EUR"); // 1.000,00 €
formatCurrency(100000, "ja-JP", "JPY"); // ¥100,000
const formatCurrency = (amount, locale, currency) =>
new Intl.NumberFormat(locale, { style: "currency", currency }).format(amount);
// Compact notation — great for dashboards
new Intl.NumberFormat("en", { notation: "compact" }).format(1500000); // 1.5M
new Intl.NumberFormat("en", { notation: "compact" }).format(8300); // 8.3K
new Intl.NumberFormat("en", { notation: "compact" }).format(980); // 980
// Percentages
new Intl.NumberFormat("en", { style: "percent" }).format(0.745); // 74.5%
// Format dates — no moment.js needed
new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
}).format(new Date());
// Thursday, April 9, 2026
// Relative time — "2 days ago", "in 3 hours"
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
rtf.format(-2, "day"); // "2 days ago"
rtf.format(3, "hour"); // "in 3 hours"
rtf.format(-1, "day"); // "yesterday"
rtf.format(1, "week"); // "next week"
✅
Intlhandles locale, currency symbols, decimal separators, RTL — all natively
❌ No moremoment.js,numeral.js, or regex hacks for basic formatting
🔁 Quick Reference Table
| Old Pattern | Modern Replacement | Why Better |
|---|---|---|
JSON.parse(JSON.stringify(obj)) |
structuredClone(obj) |
Handles Date, Map, Set, undefined |
arr.reduce() for grouping |
Object.groupBy() |
Readable, zero boilerplate |
arr[arr.length - 1] |
arr.at(-1) |
Clean negative indexing |
| isCancelled flag hacks | AbortController |
Actually cancels the network request |
document.execCommand('copy') |
navigator.clipboard.writeText() |
Async, no DOM gymnastics |
| JS-based parent styling | CSS :has() |
Pure CSS, eliminates JS entirely |
Promise.race() |
Promise.any() |
Ignores failures, picks first success |
moment.js / regex |
Intl.NumberFormat / Intl.DateTimeFormat
|
Native, locale-aware, zero dependencies |
📚 Want to Go Deeper?
If you want to master modern JavaScript and web development from scratch to production, check out:
👉 Modern Web Course — jsden.com/courses/modern-web
You'll go beyond tips and actually build things the right way with modern APIs, patterns, and real-world projects.
🎯 Final Thoughts
Modern JavaScript is powerful. You don't need a library for everything.
👉 Stop installing unnecessary packages
👉 Start using built-in APIs
👉 Your bundle size will thank you
Part 2 coming soon — covering Object.hasOwn(), .toSorted(), .with(), .findLast(), URLSearchParams, crypto.randomUUID(), and more.
Did I miss a modern API you love? Drop it in the comments 👇

Top comments (0)