Skip to content

What Vanilla JS Concepts Should You Learn Before React?

Purpose

This post explains which JavaScript concepts you should learn before diving into React. I’ve seen many developers jump straight into React without understanding JavaScript fundamentals, and they struggle to understand why React code looks the way it does.

The Problem

When I tried to learn React directly without solid JavaScript fundamentals, I got confused by code like this:

React component example
const [count, setCount] = useState(0);
function Square({ value, onSquareClick }) {
return <button onClick={onSquareClick}>{value}</button>;
}
onSquareClick={() => handleClick(0)}

I didn’t understand:

  • What those square brackets were doing
  • Why the curly braces appeared around the function parameters
  • What that arrow function syntax meant
  • How onClick was different from onclick in vanilla JS

A Reddit discussion I found showed I wasn’t alone. One user said: “Most developers these days start and end with react and barely have a clue what’s going on under the hood.” Another commented: “Typically you learn HTML, you learn the vanilla DOM API, then you move on to a framework like React.”

What I Learned

After going back to learn vanilla JavaScript fundamentals, I realized React is just JavaScript with a specific way of writing it. Here’s what I think you need to know.

1. Modern JavaScript Syntax

React uses modern JavaScript syntax throughout. I found these concepts essential.

Arrow Functions:

Arrow functions in vanilla JS
const add = (a, b) => a + b;
const greet = name => `Hello, ${name}`;
// Multi-line with explicit return
const createButton = (text) => {
const btn = document.createElement('button');
btn.textContent = text;
return btn;
};

React uses arrow functions everywhere for event handlers:

Arrow functions in React
<button onClick={() => handleClick(0)}>Click me</button>

This arrow function is equivalent to: function() { return handleClick(0); }. I needed to understand this syntax to know why we wrap function calls in arrow functions for React event handlers.

Array Destructuring:

Array destructuring in vanilla JS
const numbers = [1, 2, 3];
const [first, second] = numbers; // first=1, second=2
const [head, ...tail] = numbers; // head=1, tail=[2,3]

React’s useState hook relies on this pattern:

useState uses array destructuring
const [count, setCount] = useState(0);
// count = current value
// setCount = function to update it

I struggled with this at first because I didn’t realize useState returns an array with two elements. The destructuring syntax just extracts them into named variables.

Array Methods:

React heavily uses map(), filter(), and slice().

Common array methods
// Map creates a new array
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2); // [2, 4, 6]
// Filter keeps items that match a condition
const evens = numbers.filter(n => n % 2 === 0); // [2]
// Slice creates a copy
const copy = numbers.slice(); // [1, 2, 3]

React uses map() for rendering lists:

Rendering lists in React
const moves = history.map((squares, move) => (
<li key={move}>
<button onClick={() => jumpTo(move)}>
Go to move #{move}
</button>
</li>
));

Object Destructuring:

Object destructuring in vanilla JS
const person = { name: 'Alice', age: 30 };
const { name, age } = person; // name='Alice', age=30
// Rename properties
const { name: userName } = person; // userName='Alice'

React uses this for props:

Destructuring props in React
function Avatar({ person, size }) {
return <img src={person.imageUrl} alt={person.name} />;
}
// Without destructuring:
function Avatar(props) {
return <img src={props.person.imageUrl} alt={props.person.name} />;
}

2. DOM Manipulation Fundamentals

You don’t need to master the DOM API, but understanding these concepts helps.

Element Selection:

DOM selection
const button = document.querySelector('#myButton');
const allButtons = document.querySelectorAll('button');
const container = document.getElementById('container');

Event Handling:

Event listeners in vanilla JS
button.addEventListener('click', () => {
console.log('Button clicked!');
});
// With event object
button.addEventListener('click', (event) => {
console.log('Clicked:', event.target.textContent);
});

React’s event system is similar but handles things differently (synthetic events):

Events in React
<button onClick={(event) => {
console.log('Clicked:', event.target.textContent);
}}>Click me</button>

Creating Elements:

Creating DOM elements
const newDiv = document.createElement('div');
newDiv.textContent = 'Hello';
newDiv.className = 'greeting';
document.body.appendChild(newDiv);

React does this for you declaratively, but sometimes you still need direct DOM access with refs:

Using refs in React
function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus(); // Direct DOM API
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>Focus input</button>
</>
);
}

3. Immutability

React requires understanding why you don’t modify state directly.

Mutation in vanilla JS
const original = [1, 2, 3];
original[0] = 99; // Direct mutation
console.log(original); // [99, 2, 3]

React requires creating copies:

Immutability in React
const nextSquares = squares.slice();
nextSquares[i] = 'X';
setSquares(nextSquares);
// Wrong: Direct mutation
squares[i] = 'X'; // This won't trigger re-render
setSquares(squares);

I made this mistake early on and couldn’t figure out why my components weren’t updating. The key is: React needs to know when data changes, so you must create new objects/arrays instead of modifying existing ones.

4. Functions and Objects

React components are functions that return JSX.

Function basics
function greet(name) {
return `Hello, ${name}`;
}
// Arrow function equivalent
const greet2 = (name) => `Hello, ${name}`;
// Object properties
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
// Access properties
console.log(config.apiUrl);

React components follow this pattern:

React component as function
function Button({ label, onClick }) {
return (
<button onClick={onClick}>
{label}
</button>
);
}
// It's just a function that returns markup

5. Asynchronous JavaScript

For data fetching in React, you need to understand promises.

Promises in vanilla JS
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));

React uses useEffect for this:

Data fetching in React
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(setData)
.catch(console.error);
}, []);
return data ? <div>{data}</div> : <div>Loading...</div>;
}

Comparison: Vanilla JS vs React

Here’s how vanilla JS and React handle the same task.

Vanilla JS (imperative):

Vanilla JS DOM manipulation
const button = document.querySelector('#myButton');
button.addEventListener('click', () => {
button.textContent = 'Clicked!';
button.style.backgroundColor = 'red';
});
const list = document.querySelector('#list');
data.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
list.appendChild(li);
});

React (declarative):

React declarative approach
function ButtonList() {
const [clicked, setClicked] = useState(false);
return (
<>
<button
onClick={() => setClicked(true)}
style={{ backgroundColor: clicked ? 'red' : 'gray' }}
>
{clicked ? 'Clicked!' : 'Click me'}
</button>
<ul>
{data.map(item => <li key={item}>{item}</li>)}
</ul>
</>
);
}

React lets you describe what you want, and it figures out how to update the DOM. But you still need to understand what’s happening under the hood.

Learning Path

Based on my experience, here’s the order I recommend:

learning-path.txt
HTML Basics (1-2 days)
JavaScript Fundamentals (2-3 weeks)
- Variables, functions, objects, arrays
- Modern syntax (arrow functions, destructuring)
- Array methods (map, filter, slice, forEach)
- DOM selection and manipulation basics
- Promises and async/await
React Basics (2-3 weeks)
- Components and JSX
- Props and state
- Event handling
- Lists and keys
React Advanced (ongoing)
- Hooks (useState, useEffect, useRef, useCallback)
- State management
- Performance optimization

Why This Matters

When I learned these JavaScript concepts first, React became much easier to understand. I could recognize the patterns instead of being confused by the syntax.

Here’s what I think is most important:

  1. Debugging becomes easier: React errors often mask JavaScript mistakes. When you understand vanilla JS, you can spot when the problem is React vs. basic JavaScript.

  2. You understand the abstractions: React’s components are functions, props are parameters, state is data that changes. These are all JavaScript concepts.

  3. Escape hatches make sense: React’s refs and effects require direct DOM knowledge. Without understanding the DOM, these features seem like magic.

  4. Learning is faster: You’re not learning two things at once (React + JavaScript). You learn React patterns that build on what you already know.

What You Don’t Need to Master

I think you don’t need to become a vanilla JS expert before React. Focus on:

  • Modern syntax (ES6+)
  • Basic DOM concepts
  • Array methods
  • Functions and objects
  • Basic async handling

Skip for now:

  • Complex DOM manipulation (React handles this)
  • Browser-specific APIs (unless you need them)
  • Advanced patterns (learn these when needed)

Summary

In this post, I explained which vanilla JavaScript concepts you should learn before React: modern syntax (arrow functions, destructuring, array methods), basic DOM manipulation, functions and objects, and async handling. The key point is that React is just JavaScript with a specific way of writing it. Understanding the fundamentals makes React’s patterns clearer and helps you debug effectively. You don’t need to master vanilla JS, but learning these core concepts first will make your React journey much smoother.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments