Documentation Index
Fetch the complete documentation index at: https://mintlify.com/reatom/reatom/llms.txt
Use this file to discover all available pages before exploring further.
The @reatom/vue package provides Vue 3 integration for Reatom, allowing you to use atoms with Vue’s reactivity system through custom refs and composables.
Installation
npm install @reatom/vue @reatom/core
This package requires Vue 3.5.17 or higher and is currently in alpha.
Setup
Provide the Reatom frame to your Vue application:
import { createApp } from 'vue'
import { createContext } from '@reatom/core'
import { createReatomVue } from '@reatom/vue'
import App from './App.vue'
const ctx = createContext()
const app = createApp(App)
app.use(createReatomVue(ctx))
app.mount('#app')
Core Composables
reatomRef
Convert Reatom atoms to Vue refs for seamless integration with Vue’s reactivity system:
Basic Usage
<script setup lang="ts">
import { atom } from '@reatom/core'
import { reatomRef } from '@reatom/vue'
const countAtom = atom(0, 'count')
const count = reatomRef(countAtom)
const increment = () => {
count.value++
}
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
With Computed Atoms
Computed atoms return readonly refs:
<script setup lang="ts">
import { atom, computed } from '@reatom/core'
import { reatomRef } from '@reatom/vue'
const countAtom = atom(0, 'count')
const doubledAtom = computed(() => countAtom() * 2, 'doubled')
const count = reatomRef(countAtom)
const doubled = reatomRef(doubledAtom) // Readonly<Ref<number>>
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Doubled: {{ doubled }}</p>
<button @click="count++">Increment</button>
</div>
</template>
With Inline Computed
You can also pass computed functions directly:
<script setup lang="ts">
import { atom } from '@reatom/core'
import { reatomRef } from '@reatom/vue'
const firstNameAtom = atom('John', 'firstName')
const lastNameAtom = atom('Doe', 'lastName')
const fullName = reatomRef(() => `${firstNameAtom()} ${lastNameAtom()}`)
</script>
<template>
<div>
<p>Full name: {{ fullName }}</p>
</div>
</template>
API Reference:
reatomRef<T>(target: Atom<T>): Ref<T>
reatomRef<T>(target: Computed<T> | (() => T)): Readonly<Ref<T>>
Features:
- Automatic subscription management
- Cleanup on component unmount
- Full TypeScript support
- Readonly refs for computed atoms
useAction
Wrap functions to execute them within the Reatom context:
<script setup lang="ts">
import { atom } from '@reatom/core'
import { reatomRef, useAction } from '@reatom/vue'
const countAtom = atom(0, 'count')
const count = reatomRef(countAtom)
const increment = useAction(() => {
countAtom.set(countAtom() + 1)
})
const add = useAction((amount: number) => {
countAtom.set(countAtom() + amount)
})
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="() => add(5)">Add 5</button>
</div>
</template>
Parameters:
Returns:
- Wrapped function that executes in the Reatom context
useFrame
Access the current Reatom frame:
<script setup lang="ts">
import { useFrame } from '@reatom/vue'
import { wrap } from '@reatom/core'
const frame = useFrame()
const handleClick = () => {
wrap(() => {
// Your logic here
}, frame)
}
</script>
<template>
<button @click="handleClick">Click me</button>
</template>
Complete Examples
Counter Application
<script setup lang="ts">
import { atom } from '@reatom/core'
import { reatomRef } from '@reatom/vue'
const countAtom = atom(0, 'count')
const count = reatomRef(countAtom)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = 0
</script>
<template>
<div class="counter">
<h2>Counter: {{ count }}</h2>
<div class="buttons">
<button @click="decrement">-</button>
<button @click="reset">Reset</button>
<button @click="increment">+</button>
</div>
</div>
</template>
Todo List
<script setup lang="ts">
import { atom, computed } from '@reatom/core'
import { reatomRef, useAction } from '@reatom/vue'
import { ref } from 'vue'
interface Todo {
id: number
text: string
completed: boolean
}
const todosAtom = atom<Todo[]>([], 'todos')
const todos = reatomRef(todosAtom)
const completedCountAtom = computed(
() => todosAtom().filter(t => t.completed).length,
'completedCount'
)
const completedCount = reatomRef(completedCountAtom)
const newTodoText = ref('')
const addTodo = useAction(() => {
if (newTodoText.value.trim()) {
todosAtom.set([
...todosAtom(),
{
id: Date.now(),
text: newTodoText.value,
completed: false
}
])
newTodoText.value = ''
}
})
const toggleTodo = useAction((id: number) => {
todosAtom.set(
todosAtom().map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
)
})
const removeTodo = useAction((id: number) => {
todosAtom.set(todosAtom().filter(todo => todo.id !== id))
})
</script>
<template>
<div class="todo-app">
<h2>Todo List</h2>
<p>Completed: {{ completedCount }} / {{ todos.length }}</p>
<div class="add-todo">
<input
v-model="newTodoText"
@keyup.enter="addTodo"
placeholder="Add a todo..."
/>
<button @click="addTodo">Add</button>
</div>
<ul class="todo-list">
<li v-for="todo in todos" :key="todo.id">
<input
type="checkbox"
:checked="todo.completed"
@change="() => toggleTodo(todo.id)"
/>
<span :class="{ completed: todo.completed }">{{ todo.text }}</span>
<button @click="() => removeTodo(todo.id)">Delete</button>
</li>
</ul>
</div>
</template>
<style scoped>
.completed {
text-decoration: line-through;
opacity: 0.6;
}
</style>
<script setup lang="ts">
import { atom, computed } from '@reatom/core'
import { reatomRef, useAction } from '@reatom/vue'
const emailAtom = atom('', 'email')
const passwordAtom = atom('', 'password')
const email = reatomRef(emailAtom)
const password = reatomRef(passwordAtom)
const isValidAtom = computed(() => {
const emailValue = emailAtom()
const passwordValue = passwordAtom()
return emailValue.includes('@') && passwordValue.length >= 8
}, 'isValid')
const isValid = reatomRef(isValidAtom)
const submit = useAction(() => {
if (isValidAtom()) {
console.log('Submitting:', {
email: emailAtom(),
password: passwordAtom()
})
}
})
</script>
<template>
<form @submit.prevent="submit">
<div>
<label>Email:</label>
<input v-model="email" type="email" />
</div>
<div>
<label>Password:</label>
<input v-model="password" type="password" />
</div>
<button type="submit" :disabled="!isValid">
Submit
</button>
</form>
</template>
TypeScript Support
The package is fully typed and works seamlessly with TypeScript:
<script setup lang="ts">
import { atom, computed } from '@reatom/core'
import { reatomRef } from '@reatom/vue'
import type { Ref } from 'vue'
interface User {
id: number
name: string
email: string
}
const userAtom = atom<User | null>(null, 'user')
const user: Ref<User | null> = reatomRef(userAtom)
const userNameAtom = computed(
() => userAtom()?.name ?? 'Guest',
'userName'
)
const userName = reatomRef(userNameAtom) // Readonly<Ref<string>>
</script>
<template>
<div>
<p>Welcome, {{ userName }}</p>
</div>
</template>
Best Practices
Use reatomRef for atoms
Convert atoms to refs using reatomRef to integrate with Vue’s reactivity system.
Leverage computed atoms
Use Reatom’s computed atoms for derived state instead of Vue’s computed refs when you need cross-framework compatibility.
Wrap actions with useAction
Use useAction for event handlers to ensure they execute in the correct Reatom context.
Provide context at the app level
Set up the Reatom context once at the application root using createReatomVue.
SSR Support
For server-side rendering with Vue, ensure you create a new context per request:
import { createSSRApp } from 'vue'
import { createContext } from '@reatom/core'
import { createReatomVue } from '@reatom/vue'
export function createApp() {
const app = createSSRApp(App)
const ctx = createContext()
app.use(createReatomVue(ctx))
return { app, ctx }
}
Next Steps
- Learn about Core Concepts for atoms and state management
- Explore [Asynchttps://github.com/reatom/reatom/tree/main/packages for handling asynchronous operations
- Check out [Persisthttps://github.com/reatom/reatom/tree/main/packages for state persistence