Modern JavaScript Features You Should Be Using in 2026

March 31, 2026 · JavaScript, Web Development, ESNext

JavaScript has evolved fast in the last few years, and the gap between “what the language can do” and “what teams actually use” is still huge. If you’re shipping modern web apps in 2026, there are features you should absolutely be leaning on for clarity, safety, and performance. This guide focuses on the most practical language features you can adopt today, with concrete examples and real use cases you’ll encounter in production.

These are the tools that help you write less code, reduce edge cases, and ship faster. Most are supported in modern browsers and Node.js 20+ (recommended baseline for 2026). For older environments, you can still transpile with Babel or TypeScript.

1) Optional chaining (?.) for safe access

Optional chaining removes repetitive null checks and makes deeply nested access safe.

// Before
const theme = user && user.preferences && user.preferences.ui && user.preferences.ui.theme;

// After
const theme = user?.preferences?.ui?.theme;

This is more than shorthand. It removes a class of “Cannot read property of undefined” errors in a way that is both readable and performant.

2) Nullish coalescing (??) for defaults that respect 0 and ""

Unlike ||, which treats 0, false, and empty strings as falsy, ?? only falls back on null or undefined.

// Incorrect when 0 is a valid value
const page = input.page || 1;

// Correct
const page = input.page ?? 1;

Use this for pagination, thresholds, and feature flags where 0 or false are valid settings.

3) Logical assignment operators (||=, &&=, ??=)

Logical assignment operators make intent explicit and reduce boilerplate.

// Initialize only if undefined or null
config.timeout ??= 5000;

// Set default only if falsy
config.host ||= "localhost";

// Only assign if truthy
user.isAdmin &&= hasPermission(user);

These are great for configuration objects and caching.

4) Top-level await in modules

You can now await at the top level of ES modules in supported environments.

// config.mjs
const response = await fetch("/api/config");
export const config = await response.json();

Top-level await simplifies bootstrapping, especially in server-side tools, build pipelines, and edge functions. In browsers, it works in modern Chromium, Firefox, and Safari versions when modules are used.

5) Private class fields (#)

Private fields give true encapsulation. They are not just conventions; they are enforced by the runtime.

class TokenStore {
  #token = null;

  set(token) {
    this.#token = token;
  }

  get() {
    return this.#token;
  }
}

const store = new TokenStore();
store.#token; // SyntaxError

Use private fields in SDKs, component libraries, and any code where internal state should never be accessed externally.

6) Class static blocks

Static blocks are ideal for one-time setup without leaking helper functions into module scope.

class IdGenerator {
  static #seed;

  static {
    // One-time initialization
    this.#seed = Date.now();
  }

  static next() {
    return ++this.#seed;
  }
}

This is useful for registries, metrics, and one-off configuration.

7) Array and object destructuring (with defaults)

Destructuring is not new, but it’s still underused for cleaning up API code and configs.

function createUser({
  name,
  email,
  role = "user",
  settings: { theme = "light" } = {}
}) {
  return { name, email, role, theme };
}

When working with JSON payloads, consider running examples through the JSON Formatter to quickly inspect shapes and spot missing fields.

8) Object.hasOwn for safe property checks

Use Object.hasOwn instead of obj.hasOwnProperty to avoid prototype issues.

const data = Object.create(null);

Object.hasOwn(data, "value"); // false

// Safer than data.hasOwnProperty("value") which would fail

This is especially helpful when dealing with untrusted objects or dictionaries created without prototypes.

9) Array.findLast and Array.findLastIndex

When you need the last match, these are cleaner and faster than reversing arrays or looping manually.

const lastError = logs.findLast(l => l.level === "error");
const lastErrorIndex = logs.findLastIndex(l => l.level === "error");

Great for time-ordered logs, chat messages, and event streams.

10) Promise.any and Promise.allSettled

Modern async workflows should use the right promise combinator for the job.

// Return the first successful response
const data = await Promise.any([fetchA(), fetchB(), fetchC()]);

// Wait for all results, even failures
const results = await Promise.allSettled([task1(), task2(), task3()]);

Promise.any is ideal for multi-region fetches. Promise.allSettled helps with batch processing where you want a full report instead of failing fast.

11) String.replaceAll and named capture groups

ReplaceAll and named capture groups make text operations safer and more readable.

const template = "Hello, {{name}}!";
const output = template.replaceAll("{{name}}", "Jesse");

const regex = /user:(?<id>\d+):(?<role>\w+)/;
const { groups } = regex.exec("user:42:admin");
// groups.id === "42", groups.role === "admin"

For regex-heavy workflows, use the Regex Tester to validate patterns and test edge cases quickly.

12) Intl APIs for formatting and collation

The Intl APIs are powerful and built-in. Use them instead of custom formatting libraries whenever possible.

const price = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 2
}).format(1299.5);

// "$1,299.50"

Also useful: Intl.DateTimeFormat, Intl.RelativeTimeFormat, and Intl.Collator for locale-aware sorting.

13) URL and URLSearchParams

Stop building query strings by hand. The URL APIs are safer and eliminate encoding mistakes.

const url = new URL("https://api.example.com/search");
url.searchParams.set("q", "modern js");
url.searchParams.set("page", "2");

// https://api.example.com/search?q=modern+js&page=2

For debugging encoded strings, the URL Encoder/Decoder is a fast way to verify your output.

14) Structured cloning with structuredClone()

Need to deep copy objects without JSON stringify hacks? Use structuredClone.

const original = { user: { id: 1 }, list: [1, 2, 3] };
const copy = structuredClone(original);
copy.user.id = 2;

// original.user.id remains 1

It handles Maps, Sets, Dates, and ArrayBuffers correctly, unlike JSON-based clones.

15) BigInt for safe large integers

When you need exact precision (IDs, timestamps, crypto), use BigInt.

const big = 9007199254740993n; // Beyond Number.MAX_SAFE_INTEGER
const next = big + 2n;

Remember: you cannot mix BigInt and Number without explicit conversion.

16) The modern module system (ESM)

ES modules are the default in 2026. Prefer import / export in both browser and Node environments.

// utils.mjs
export function add(a, b) { return a + b; }

// app.mjs
import { add } from "./utils.mjs";

In Node.js, set "type": "module" in package.json or use .mjs extension.

17) Using Web Crypto for secure random values

For cryptographic random values, use crypto.getRandomValues instead of Math.random().

const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);

const hex = Array.from(bytes, b => b.toString(16).padStart(2, "0")).join("");

If you just need UUIDs quickly, you can generate them with the UUID Generator for fast copy/paste during development.

18) Native Base64 with atob/btoa (and TextEncoder)

For Base64 encoding in browser contexts, use btoa/atob or TextEncoder for UTF-8 safety.

const text = "Hello 🌍";
const bytes = new TextEncoder().encode(text);
const base64 = btoa(String.fromCharCode(...bytes));

When you need a quick manual check, the Base64 Encoder/Decoder is a handy reference.

Practical adoption checklist (2026-ready)

Key takeaways

FAQ

Are these features safe to use in production in 2026? Yes, they are safe in all evergreen browsers and Node.js 20+ environments, which cover the vast majority of production deployments in 2026.

Do I still need Babel for modern JavaScript? Yes, you still need Babel if you support legacy browsers or if your build pipeline targets older runtimes that lack features like top-level await or private fields.

What Node.js version should I target? Node.js 20 LTS is the recommended baseline because it fully supports most modern language features and has active security updates.

Is structuredClone better than JSON.parse(JSON.stringify())? Yes, structuredClone preserves Maps, Sets, Dates, and ArrayBuffers and avoids data loss from JSON serialization.

How do I validate my JSON payloads when using modern syntax? You should use a formatter like the JSON Formatter to quickly inspect and validate payload shapes during development.

Recommended Tools & Resources

Level up your workflow with these developer tools:

Cloudflare Workers → Vercel → Clean Code by Robert C. Martin →

Dev Tools Digest

Get weekly developer tools, tips, and tutorials. Join our developer newsletter.