When building dynamic interfaces, especially where users can make changes (text editors, or design tools), providing undo and redo functionality dramatically improves user experience.

Thankfully, Vue + VueUse makes this a breeze with the useRefHistory composable.

🔄 What is useRefHistory?
useRefHistory from the @vueuse/core package allows you to track the history of changes to a ref in Vue. It enables you to programmatically undo or redo changes made to that ref, just like in text editors or drawing apps.

💡 Let's Build a Simple To-do App with Undo/Redo

In this example, we have a list of to-do items. Every time you add or remove an item, the change is tracked — and you can go back and forth using Undo and Redo buttons.

Project UI

🧠 The Key Code:

import { ref } from "vue";
import { useRefHistory } from "@vueuse/core";

const todoItems = ref(["walk my dog", "go to the gym", "eat lunch"]);
const add = (e) => {
  todoItems.push(e.target.value);
  e.target.value = "";
};
const remove = (index) => todoItems.value.splice(index, 1);

// 👇 Track changes to `todoItems` and enable undo/redo
const { undo, redo } = useRefHistory(todoItems, {
  deep: true,
});


  
    Undo
    Redo

    
    
      
        {{ item }}
        Remove

Here it is in action:

image showing ref history in action

🛠 Use Cases for useRefHistory
Here are a few places where useRefHistory can be a game changer:

  • Forms: Revert user inputs step-by-step (e.g., a multi-field form with preview).

  • Design Tools: Let users undo shapes, colors, or movements in a canvas.

  • Text Editors: Implement undo/redo in markdown or WYSIWYG editors.

  • Games: Go back to previous states (e.g., board games like chess).

  • Collaborative Tools: Temporarily revert shared content locally before syncing changes.

📝 Final Thoughts
useRefHistory is a simple yet powerful utility that adds professional polish to your Vue apps. Whether you're building productivity tools or just want to offer a better user experience, this is definitely one to have in your toolbox.

Want to see the full code? Try it yourself here!