You can, but have you tried it?

The biggest difference is: Before React/Vue, you usually told the UI how to change based on some action. Which is fine, but gets complex really quickly. Take something simple like a login form. Assume for a moment that there is a REST API for logging you in, so there’s no server-side rendered version of your login form. Now you want to make things a bit nicer for the user:

Obviously, the login form needs to be send via AJAX, because the REST API response delivers JSON and you don’t want to display raw JSON to the user.

There are two error states: no account with that email, wrong password. Assuming you take advantage of browser capabilities such as <input type="email" required> so you don't have to check this stuff in JS (although you might for styling reasons).

Your login form has 2 labels, 2 input boxes and a submit button.

Of course, while your send your login request via AJAX, you want to disable the login button, show a loading spinner, change the color of the button to the "disabled state". If there was an error, you want to re-enable the button, change its color and hide the loading spinner. Depending on the login error, you want to update the label text, the label color, the input border color. If the user enters something, you might want to update these three again so that it doesn't look like the new input still has an error. If the user entered the correct email, but a wrong password, you now need to: update all three to its original non-error state: the email label color, email label text, email input border color. You also want to update all three of the password field.

Futhermore, later you might introduce a toggle "show password", because people on mobile make mistakes when they enter their password. This turns the <input type="password"> into an <input type="text"> (or reverts it). If you click the toggle button, you also want to change the icon or the text of the button. Futhermore, if you submit the form, you might want to hide the password again. Whatever.

See, all of this can be done traditionally with server-side rendering. Some parts would be just fine, other parts are simply less convenient for the user. Sometimes, you don't get to decide to do it on the server, so you have to do it on the client in JS.

Now, do you want to tell the UI which part needs to update depending on what state you have? That is the traditional jQuery-style DOM manipulation way of writing UI code. In this very simple case of a login form, you usually have to update 6-8 elements for a very simple change in the state. This gets messy very fast.

What React/Vue/etc. solve is that you don't tell the UI how to update the DOM elements. You just say: newState = { emailError: 'Account does not exist', 'passwordError': null, formIsSubmitting: false } and write your UI so that it reacts to the state change.

Can you write this reactive code without a framework? Yes, you could, but honestly, give it a try. The fundamental question would be: Do you "hook" your code to the existing DOM or do you create all DOM elements on the fly in JS? Assuming the latter, you now have to write a lot of "document.createElement" calls. But do you want to re-create all elements when the state updates? You might lose stuff like the caret position in an input field. You might have to re-assign event listeners or whatever.

Thing is: Writing UI code that offers a great experience to the end user was never super easy to begin with.