# Reactivity Transform {#reactivity-transform}
:::danger Removed Experimental Feature
Reactivity Transform was an experimental feature, and has been removed in the latest 3.4 release. Please read about [the reasoning here](https://github.com/vuejs/rfcs/discussions/369#discussioncomment-5059028).
If you still intend to use it, it is now available via the [Vue Macros](https://vue-macros.sxzz.moe/features/reactivity-transform.html) plugin.
:::
:::tip Composition-API-specific
Reactivity Transform is a Composition-API-specific feature and requires a build step.
:::
## Refs vs. Reactive Variables {#refs-vs-reactive-variables}
Ever since the introduction of the Composition API, one of the primary unresolved questions is the use of refs vs. reactive objects. It's easy to lose reactivity when destructuring reactive objects, while it can be cumbersome to use `.value` everywhere when using refs. Also, `.value` is easy to miss if not using a type system.
[Vue Reactivity Transform](https://github.com/vuejs/core/tree/main/packages/reactivity-transform) is a compile-time transform that allows us to write code like this:
```vue
```
The `$ref()` method here is a **compile-time macro**: it is not an actual method that will be called at runtime. Instead, the Vue compiler uses it as a hint to treat the resulting `count` variable as a **reactive variable.**
Reactive variables can be accessed and re-assigned just like normal variables, but these operations are compiled into refs with `.value`. For example, the `
```
The above will be compiled into the following runtime declaration equivalent:
```js
export default {
props: {
msg: { type: String, required: true },
count: { type: Number, default: 1 },
foo: String
},
setup(props) {
watchEffect(() => {
console.log(props.msg, props.count, props.foo)
})
}
}
```
## Retaining Reactivity Across Function Boundaries {#retaining-reactivity-across-function-boundaries}
While reactive variables relieve us from having to use `.value` everywhere, it creates an issue of "reactivity loss" when we pass reactive variables across function boundaries. This can happen in two cases:
### Passing into function as argument {#passing-into-function-as-argument}
Given a function that expects a ref as an argument, e.g.:
```ts
function trackChange(x: Ref) {
watch(x, (x) => {
console.log('x changed!')
})
}
let count = $ref(0)
trackChange(count) // doesn't work!
```
The above case will not work as expected because it compiles to:
```ts
let count = ref(0)
trackChange(count.value)
```
Here `count.value` is passed as a number, whereas `trackChange` expects an actual ref. This can be fixed by wrapping `count` with `$$()` before passing it:
```diff
let count = $ref(0)
- trackChange(count)
+ trackChange($$(count))
```
The above compiles to:
```js
import { ref } from 'vue'
let count = ref(0)
trackChange(count)
```
As we can see, `$$()` is a macro that serves as an **escape hint**: reactive variables inside `$$()` will not get `.value` appended.
### Returning inside function scope {#returning-inside-function-scope}
Reactivity can also be lost if reactive variables are used directly in a returned expression:
```ts
function useMouse() {
let x = $ref(0)
let y = $ref(0)
// listen to mousemove...
// doesn't work!
return {
x,
y
}
}
```
The above return statement compiles to:
```ts
return {
x: x.value,
y: y.value
}
```
In order to retain reactivity, we should be returning the actual refs, not the current value at return time.
Again, we can use `$$()` to fix this. In this case, `$$()` can be used directly on the returned object - any reference to reactive variables inside the `$$()` call will retain the reference to their underlying refs:
```ts
function useMouse() {
let x = $ref(0)
let y = $ref(0)
// listen to mousemove...
// fixed
return $$({
x,
y
})
}
```
### Using `$$()` on destructured props {#using-on-destructured-props}
`$$()` works on destructured props since they are reactive variables as well. The compiler will convert it with `toRef` for efficiency:
```ts
const { count } = defineProps<{ count: number }>()
passAsRef($$(count))
```
compiles to:
```js
setup(props) {
const __props_count = toRef(props, 'count')
passAsRef(__props_count)
}
```
## TypeScript Integration {#typescript-integration}
Vue provides typings for these macros (available globally) and all types will work as expected. There are no incompatibilities with standard TypeScript semantics, so the syntax will work with all existing tooling.
This also means the macros can work in any files where valid JS / TS are allowed - not just inside Vue SFCs.
Since the macros are available globally, their types need to be explicitly referenced (e.g. in a `env.d.ts` file):
```ts
///
```
When explicitly importing the macros from `vue/macros`, the type will work without declaring the globals.
## Explicit Opt-in {#explicit-opt-in}
:::danger No longer supported in core
The following only applies up to Vue version 3.3 and below. Support has been removed in Vue core 3.4 and above, and `@vitejs/plugin-vue` 5.0 and above. If you intend to continue using the transform, please migrate to [Vue Macros](https://vue-macros.sxzz.moe/features/reactivity-transform.html) instead.
:::
### Vite {#vite}
- Requires `@vitejs/plugin-vue@>=2.0.0`
- Applies to SFCs and js(x)/ts(x) files. A fast usage check is performed on files before applying the transform so there should be no performance cost for files not using the macros.
- Note `reactivityTransform` is now a plugin root-level option instead of nested as `script.refSugar`, since it affects not just SFCs.
```js
// vite.config.js
export default {
plugins: [
vue({
reactivityTransform: true
})
]
}
```
### `vue-cli` {#vue-cli}
- Currently only affects SFCs
- Requires `vue-loader@>=17.0.0`
```js
// vue.config.js
module.exports = {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => {
return {
...options,
reactivityTransform: true
}
})
}
}
```
### Plain `webpack` + `vue-loader` {#plain-webpack-vue-loader}
- Currently only affects SFCs
- Requires `vue-loader@>=17.0.0`
```js
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
reactivityTransform: true
}
}
]
}
}
```