·
1 min read

Pinia Tip: Simple History With Undo and Redo Functionality

Pinia Tip: Simple History With Undo and Redo Functionality Image

I recently needed to implement a simple history with undo and redo functionality for a project I'm working on. I decided to use the useRefHistory composable from VueUse to implement this functionality.

Defining the store

Let's first take a look at the store we use for demonstration purposes which is a simple counter store:

stores/counter.ts
import { ref } from 'vue'; import { defineStore } from 'pinia'; export const useCounterStore = defineStore('counter', () => { const count = ref(0); function increment() { count.value++; } function decrement() { count.value--; } return { count, increment, decrement }; });

Now let's use that store in a Vue component:

components/Counter.vue
<script setup lang="ts"> const counterStore = useCounterStore(); const { count } = storeToRefs(counterStore); </script> <template> <div class="container"> <span>count is {{ counterStore.count }}</span> <button @click="counterStore.increment">Increment</button> <button @click="counterStore.decrement">Decrement</button> </div> </template>

Implementing the history

Now that we have a store, let's implement the history functionality. We can do this by using the useRefHistory composable from VueUse:

components/Counter.vue
<script setup lang="ts"> const counterStore = useCounterStore(); const { count } = storeToRefs(counterStore); const { history, undo, redo } = useRefHistory(count, { capacity: 50, // limit history records }); </script> <template> <div class="container"> <span>count is {{ counterStore.count }}</span> <button @click="counterStore.increment">Increment</button> <button @click="counterStore.decrement">Decrement</button> </div> <div class="container" style="margin-top: 20px"> <span>History</span> <button @click="undo">Undo</button> <button @click="redo">Redo</button> <ul> <li v-for="entry of history" :key="entry.timestamp"> <span>{{ entry }}</span> </li> </ul> </div> </template>

Demo

Try it yourself in the following StackBlitz:

If you liked this Vue tip, follow me on X to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue & Nuxt newsletter:

I will never share any of your personal data. You can unsubscribe at any time.