With plain DOM code you write imperative updates: when a todo is added, create an <li>, append it, then find the button and change its label. Every feature adds another place that must update every affected element, and bugs appear when one is forgotten. Frameworks flip this around. You describe the interface declaratively as a function of state (view = f(state)), change the state, and the framework works out which DOM operations are needed.
Three ideas recur in every framework in this section:
Components bundle markup, styling and behavior into reusable units that take inputs (props) and emit events.
State is the data that changes over time. Local state lives in one component; shared state lives in a store (Redux 3,824 , Pinia 5,482 , NgRx 924,431 ) or in context.
Rendering is how state changes reach the DOM. This is where frameworks differ most.

Svelte 36,701 and Angular also compile templates into JavaScript that updates DOM nodes directly. A common interview question is "Is the virtual DOM fast?" Honest answer: fast enough and predictable, but it does work that signal-based frameworks skip.