Introduction
If you learned JavaScript from an older tutorial, there's a decent chance you're writing more code than necessary without realizing it. Modern JavaScript (officially called ES6 and beyond, first released in 2015) added features that make everyday code shorter, more readable, and honestly just more enjoyable to write.
If you're using React, you're already using several of these features without knowing their names. Let's fix that.
1. Arrow Functions
// Old way
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
For single-expression functions, you can skip the curly braces and return keyword entirely — the expression's result is returned automatically.
Where you'll actually use this: callbacks, all the time.
const doubled = numbers.map(num => num * 2);
2. Destructuring
Pulling values out of objects or arrays into individual variables.
const user = { name: "Farhan", role: "Developer" };
// Old way
const name = user.name;
const role = user.role;
// Destructuring
const { name, role } = user;
Works with arrays too:
const [first, second] = ["React", "Tailwind"];
This is exactly what's happening every time you write const [count, setCount] = useState(0) in React.
3. Spread and Rest Operators (...)
The same three dots, doing two different jobs depending on context.
Spread — expands an array or object:
const original = [1, 2, 3];
const copy = [...original, 4]; // [1, 2, 3, 4]
const user = { name: "Farhan" };
const updatedUser = { ...user, role: "Developer" };
Rest — collects multiple values into one array/object:
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
4. Template Literals
String building using backticks instead of concatenation:
const name = "Farhan";
// Old way
const greeting = "Hello, " + name + "! You have " + count + " messages.";
// Template literal
const greeting = `Hello, ${name}! You have ${count} messages.`;
Also supports multi-line strings naturally, without \n characters.
5. Optional Chaining (?.)
Safely access deeply nested properties without your code crashing if something is missing:
// Old way — risky if 'address' doesn't exist
const city = user.address.city;
// Optional chaining — returns undefined instead of crashing
const city = user?.address?.city;
Extremely useful when working with API data, where fields aren't always guaranteed to exist.
6. Nullish Coalescing (??)
Provides a fallback value, but only when the original value is null or undefined (unlike ||, which also triggers on 0, "", or false):
const count = userCount ?? 0;
If userCount is 0, || would incorrectly fall back to the default. ?? correctly keeps it as 0.
7. Array Methods: map, filter, reduce
The trio that replaces almost every manual for loop you'd otherwise write:
const users = [
{ name: "Ali", active: true },
{ name: "Sara", active: false },
];
const activeNames = users
.filter(user => user.active)
.map(user => user.name);
// ["Ali"]
filter keeps items matching a condition. map transforms each item. reduce combines everything into a single value (a sum, an object, whatever you need).
Common Beginner Mistakes
- Mixing arrow functions and this incorrectly. Arrow functions don't have their own this — they inherit it from the surrounding scope. This trips people up in class components or event handlers occasionally.
- Overusing optional chaining to hide real bugs. ?. is for genuinely optional data, not a substitute for fixing broken logic upstream.
- Forgetting map returns a new array. It doesn't modify the original array — a common source of "why isn't my state updating" bugs in React.
Final Thoughts
These seven features cover probably 90% of what "modern JavaScript" actually means in day-to-day frontend work. If you're comfortable with all of them, reading React code (and most modern codebases) gets significantly easier, because this is the syntax nearly everyone writes in now.
Muhammad Farhan is a Frontend Developer specializing in React.js and Tailwind CSS, based in Dera Ismail Khan, Pakistan.
Portfolio: Muhammad Farhan