There's a major hurdle for people learning front-end web development today. Because recruiters are looking for people with skills in one or more UI libraries (e.g. React), new developers are forced to skim through the basics and learn a UI library as soon as possible. As Jeremy Keith writes in his article User agents:

I sometimes get asked about what a new starter should learn. On the podcast, I mentioned a post I wrote a while back with links to some great resources and tutorials. As I said then:

For web development, start with HTML, then CSS, then JavaScript (and don’t move on to JavaScript too quickly—really get to grips with HTML and CSS first).

That’s assuming you want to be a good well-rounded web developer. But it might be that you need to get a job as quickly as possible. In that case, my advice would be very different. I would advise you to learn React.

Believe me, I take no pleasure in giving that advice. But given the reality of what recruiters are looking for, knowing React is going to increase your chances of getting a job (something that’s reflected in the curricula of coding schools). And it’s always possible to work backwards from React to the more fundamental web technologies of HTML, CSS, and JavaScript. I hope.

By making state-based UI more popular, I think UI libraries have done a good thing for the Web, in the same way that jQuery inspired much of ES5. State-based UI makes highly dynamic applications possible where handwritten vanilla JS would be too complex. As Rich Harris says in Rethinking reactivity, Frameworks are not tools for organizing your code, they are tools for organizing your mind. But I think it's a shame that beginners are forced to learn these tools without a solid foundation.

I want to help you understand how these tools work in the same way that I wanted to understand how jQuery worked. You might have heard that some of these libraries use a virtual DOM. What does this mean? Before we answer that, I'm going to quote Rich Harris again, this time from his article Virtual DOM is pure overhead:

It's important to understand that virtual DOM isn't a feature. It's a means to an end, the end being declarative, state-driven UI development. Virtual DOM is valuable because it allows you to build apps without thinking about state transitions, with performance that is generally good enough. That means less buggy code, and more time spent on creative tasks instead of tedious ones.

The virtual DOM is an object that describes how the real DOM should look based on the current state of your application.

Now you may be wondering: what is “state”? This time I'll defer to my friend Chris Ferdinandi in When should you use a state-based UI library?

One of the primary concepts behind JavaScript web apps is state.

If you’ve never heard that word state before in JavaScript—or have but don’t know what it means—you’re not alone!

State is just data. So why do they call it state instead of data? Because there’s a time-bound aspect to it.

State is data at a particular moment in time. It’s the present “state” of your data. Get it?

Today, many web apps use state to generate their UI. With a state-based UI approach, you store all of the data for your user interface in a JavaScript object. Then, you use JavaScript to build the DOM based on the current state of the data.

Let's look at a simple Counter component written in React. It renders a counter with buttons for incrementing and decrementing its value:

function Counter() {
const [count, setCount] = useState(0);

const VDOM = (
<Fragment>
<p>Counter: {count}</p>
<button type="button" onClick={increment}>
Increment
</button>
<button type="button" onClick={decrement}>
Decrement
</button>
</Fragment>
);

function increment() {
setCount(count + 1);
}

function decrement() {
setCount(count - 1);
}

console.log(VDOM);

return VDOM;
}

Demo: Counter with JSX.

The thing we're returning is the virtual DOM. It's written in JSX, a syntax extension to JavaScript that makes it easier to describe what our UI should look like. JSX seems like magic because it looks like we're returning HTML markup directly from our JavaScript function. But we aren't. The JSX code is compiled to a series of nested calls to the React.createElement() method:

function Counter() {
const [count, setCount] = React.useState(0);

const VDOM = React.createElement(
React.Fragment,
null,
React.createElement("p", null, "Counter: ", count),
React.createElement(
"button",
{ type: "button", onClick: increment },
"Increment"
),
React.createElement(
"button",
{ type: "button", onClick: decrement },
"Decrement"
)
);

function increment() {
setCount(count + 1);
}

function decrement() {
setCount(count - 1);
}

console.log(VDOM);

return VDOM;
}

Demo: Counter without JSX.

The first argument to the React.createElement() method is the type of element to create, much like the native document.createElement() method. The second argument is an object of props, short for properties. These are a bit like HTML attributes. Any subsequent arguments are children of the element. These are made available via the special .children prop. The return value is a small piece of virtual DOM: an object describing how the element should look in the real DOM.

Whether we use JSX or not, the thing we're returning is the virtual DOM. In both examples, we're logging it to the console before returning it. I've simplified it a little, but this is what the output looks like:

const VDOM = {
type: Symbol(react.fragment),
props: {
children: [
{
type: "p",
props: {
children: ["Counter: ", 0],
},
},
{
type: "button",
props: {
type: "button",
children: "Increment",
onClick: ƒ,
},
},
{
type: "button",
props: {
type: "button",
children: "Decrement",
onClick: ƒ,
},
},
],
},
};

Every time our Counter component is updated, e.g. when the count state changes, React will create a new virtual DOM. React's job is to compare the new one with the old one, figure out what changed, then apply the changes to the real DOM. All we have to do is describe how our UI should look based on our data.

Again, the virtual DOM is not a feature. It's a means to an end. I'm personally not a fan of the React API, and I think React is a poor choice for performance in 2022. It's fine if you like it, but I encourage you to try alternatives like Solid, Preact, and Inferno. They offer similar APIs with significantly improved performance. This is good for your business and, more importantly, your users.

For more on the virtual DOM, I recommend the Virtual DOM chapter in the Preact tutorial and the aforementioned article by Rich Harris.