Vue.js is a progressive JavaScript framework designed primarily for building user interfaces. Its progressive nature means it can be adopted incrementally, allowing developers to use as much or as little of the framework as their project requires, and it focuses on the view layer so it can be integrated smoothly with other libraries or dropped into an existing codebase. The centerpiece of Vue's power is its reactivity system, which automatically tracks dependencies in your data and updates the DOM whenever reactive state changes. In Vue 2, this tracking was implemented with Object.defineProperty to intercept property access, but Vue 3 replaced that approach with Proxy objects, which provide a more complete and efficient way to intercept get and set operations on reactive data.
To create reactive state in Vue 3, the Composition API provides two primary functions: reactive() and ref(). The reactive() function takes an object and returns a deeply reactive proxy of the original, so you can read and write its properties directly, such as state.count. The ref() function, on the other hand, wraps a single value—either a primitive or an object—and requires the .value accessor when used inside a script. Refs are automatically unwrapped when used inside templates, so the .value is not needed there. The choice between them often comes down to ergonomics: ref() is more flexible because it can hold primitives like numbers and strings, while reactive() is convenient for grouping related state into an object that can be accessed without .value.
Both reactive() and ref() integrate seamlessly with the rest of the Vue reactivity system, so any component or computed property that reads from them will automatically re-render when their values change. This makes it easy to model state in whichever shape best fits the problem at hand, and to compose pieces of state across components without manually wiring up subscriptions or update notifications.