DEV Community

Nagendra Namburi
Nagendra Namburi

Posted on

🚫 Stop Writing Old JavaScript — ✅ Start Using Modern Built-in APIs (Part 1)

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:

  • lodash for 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));
Enter fullscreen mode Exit fullscreen mode

⚠️ 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 {}
Enter fullscreen mode Exit fullscreen mode

✅ 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 ✅
Enter fullscreen mode Exit fullscreen mode

🧠 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);
};
Enter fullscreen mode Exit fullscreen mode

✅ 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;
}, {});
Enter fullscreen mode Exit fullscreen mode

✅ Modern way

const grouped = Object.groupBy(users, user => user.role);
Enter fullscreen mode Exit fullscreen mode

🧠 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" }]
Enter fullscreen mode Exit fullscreen mode
// 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 }]
Enter fullscreen mode Exit fullscreen mode

🔁 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
Enter fullscreen mode Exit fullscreen mode

✅ Modern way

const last = arr.at(-1);
Enter fullscreen mode Exit fullscreen mode

🧠 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

✅ Modern way

const controller = new AbortController();

fetch("/api/data", { signal: controller.signal });

// Actually cancels the network request
controller.abort();
Enter fullscreen mode Exit fullscreen mode

🧠 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);
  }
});
Enter fullscreen mode Exit fullscreen mode
// 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
}, []);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

✅ Modern way

await navigator.clipboard.writeText("Hello World");
Enter fullscreen mode Exit fullscreen mode

🧠 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;
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Requires HTTPS or localhost
⚠️ User must grant clipboard permission for readText()


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
  }
});
Enter fullscreen mode Exit fullscreen mode

✅ Modern way — pure CSS

.card:has(img) {
  border: 2px solid green;
}
Enter fullscreen mode Exit fullscreen mode

🧠 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;
}
Enter fullscreen mode Exit fullscreen mode

✅ 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"),
]);
Enter fullscreen mode Exit fullscreen mode

✅ Modern way

// Returns first SUCCESS — ignores failures ✅
const result = await Promise.any([
  fetch("/fast"),
  fetch("/slow")
]);
Enter fullscreen mode Exit fullscreen mode

🧠 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());
Enter fullscreen mode Exit fullscreen mode
// 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);
  }
}
Enter fullscreen mode Exit fullscreen mode

📊 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, ",");
}
Enter fullscreen mode Exit fullscreen mode

✅ Modern way

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
}).format(1000);
// $1,000.00
Enter fullscreen mode Exit fullscreen mode

🧠 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);
Enter fullscreen mode Exit fullscreen mode
// 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
Enter fullscreen mode Exit fullscreen mode
// Percentages
new Intl.NumberFormat("en", { style: "percent" }).format(0.745); // 74.5%
Enter fullscreen mode Exit fullscreen mode
// 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
Enter fullscreen mode Exit fullscreen mode
// 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"
Enter fullscreen mode Exit fullscreen mode

Intl handles locale, currency symbols, decimal separators, RTL — all natively
❌ No more moment.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)