# How Does the Undo/Redo Functionality Work with Pinia Store in Vue Color Avatar

> Learn how Pinia store enables unlimited undo/redo in Vue Color Avatar. Explore time travel history with past present and future state arrays for seamless editing.

- Repository: [LeoKu/vue-color-avatar](https://github.com/codennnn/vue-color-avatar)
- Tags: internals
- Published: 2026-02-27

---

**The vue-color-avatar editor implements time-travel history using a Pinia store that maintains `past`, `present`, and `future` arrays, enabling unlimited undo and redo through state mutation actions.**

The vue-color-avatar repository provides a vector-style avatar generator built with Vue 3 and TypeScript. Understanding how the undo/redo functionality works with Pinia store reveals a classic command-pattern implementation that tracks state changes through immutable history stacks.

## Understanding the History State Structure

The Pinia store defined in [`src/store/index.ts`](https://github.com/codennnn/vue-color-avatar/blob/main/src/store/index.ts) structures history as three distinct collections that represent the timeline of user interactions.

| Property | Purpose |
|----------|---------|
| `past` | Array of previous `AvatarOption` values in chronological order |
| `present` | The current `AvatarOption` actively displayed in the UI |
| `future` | Array of `AvatarOption` values that were undone and can be restored |

```typescript
// src/store/index.ts (lines 16-20)
export interface State {
  history: {
    past: AvatarOption[]
    present: AvatarOption
    future: AvatarOption[]
  }
  isSiderCollapsed: boolean
}

```

## Recording State Changes with SET_AVATAR_OPTION

When users modify avatar settings, the `[SET_AVATAR_OPTION]` action updates the history stack. This action implements the core logic for branching timelines by clearing the `future` array whenever a new state is committed.

```typescript
// src/store/index.ts (lines 35-41)
[SET_AVATAR_OPTION](data: AvatarOption) {
  this.history = {
    past: [...this.history.past, this.history.present],
    present: data,
    future: [],
  }
}

```

**Key behavior:** The spread operator creates immutable copies of the `past` array, ensuring Reactivity works correctly while the `future` array resets to empty. This prevents redo operations from jumping to obsolete states after new modifications.

## Implementing Undo and Redo Actions

The store exposes `[UNDO]` and `[REDO]` actions that manipulate the three history collections to navigate through the state timeline.

### The UNDO Action

The `[UNDO]` action moves the current state into the `future` stack and restores the most recent previous state.

```typescript
// src/store/index.ts (lines 43-52)
[UNDO]() {
  if (this.history.past.length > 0) {
    const previous = this.history.past[this.history.past.length - 1]
    const newPast = this.history.past.slice(0, this.history.past.length - 1)
    this.history = {
      past: newPast,
      present: previous,
      future: [this.history.present, ...this.history.future],
    }
  }
}

```

**Execution flow:**
1. Verify `past` contains at least one entry
2. Extract the last element as the `previous` state
3. Slice `past` to remove the retrieved element
4. Prepend current `present` to `future` array
5. Assign `previous` as new `present`

### The REDO Action

The `[REDO]` action reverses an undo by shifting states from `future` back to `present` and pushing the current state onto `past`.

```typescript
// src/store/index.ts (lines 55-64)
[REDO]() {
  if (this.history.future.length > 0) {
    const next = this.history.future[0]
    const newFuture = this.history.future.slice(1)
    this.history = {
      past: [...this.history.past, this.history.present],
      present: next,
      future: newFuture,
    }
  }
}

```

**Execution flow:**
1. Verify `future` contains at least one entry
2. Extract the first element as the `next` state
3. Slice `future` to remove the retrieved element
4. Append current `present` to `past` array
5. Assign `next` as new `present`

## Integrating Undo/Redo in Vue Components

Components interact with the history system through the store's typed actions and reactive state. The mutation constants in [`src/store/mutation-type.ts`](https://github.com/codennnn/vue-color-avatar/blob/main/src/store/mutation-type.ts) provide type-safe access to action names.

```typescript
// Component integration example
import { useStore } from '@/store'
import { SET_AVATAR_OPTION, UNDO, REDO } from '@/store/mutation-type'

const store = useStore()

// Apply a new avatar configuration
function updateAvatar(option: AvatarOption) {
  store[SET_AVATAR_OPTION](option)
}

// History navigation handlers
function handleUndo() {
  store[UNDO]()
}

function handleRedo() {
  store[REDO]()
}

```

### Checking History Availability

UI controls should disable undo/redo buttons when the respective history stacks are empty. Compute these states using the `past` and `future` array lengths.

```typescript
import { computed } from 'vue'

const canUndo = computed(() => store.history.past.length > 0)
const canRedo = computed(() => store.history.future.length > 0)

```

## Summary

- The **Pinia store** in [`src/store/index.ts`](https://github.com/codennnn/vue-color-avatar/blob/main/src/store/index.ts) implements undo/redo through three reactive arrays: `past`, `present`, and `future`.
- **Immutable updates** using the spread operator ensure Reactivity works correctly when shifting states between history stacks.
- **Branching timeline logic** clears the `future` array whenever `[SET_AVATAR_OPTION]` commits a new state, preventing invalid redo operations.
- **Type-safe actions** use constants from [`src/store/mutation-type.ts`](https://github.com/codennnn/vue-color-avatar/blob/main/src/store/mutation-type.ts) to expose `[UNDO]` and `[REDO]` methods that manipulate the history collections.
- **UI integration** relies on computed properties checking `store.history.past.length` and `store.history.future.length` to enable or disable history controls.

## Frequently Asked Questions

### How is the history state initialized in the Pinia store?

The history state initializes with an empty `past` array, a `present` value generated by `getRandomAvatarOption()` from [`src/utils/index.ts`](https://github.com/codennnn/vue-color-avatar/blob/main/src/utils/index.ts), and an empty `future` array. This setup provides a valid initial avatar configuration while maintaining empty history stacks ready to record user interactions.

### Why does the future array get cleared when setting a new avatar option?

The `[SET_AVATAR_OPTION]` action clears the `future` array to maintain a linear timeline. When users undo several steps and then make a new change, the previous future states become invalid because they represent a timeline that no longer exists. Clearing `future` prevents the UI from offering redo operations that would jump to obsolete configurations.

### Can the undo/redo history be limited to a specific number of steps?

The current implementation in [`src/store/index.ts`](https://github.com/codennnn/vue-color-avatar/blob/main/src/store/index.ts) does not impose a limit on the `past` or `future` array lengths. However, you could extend the `[SET_AVATAR_OPTION]` action to check `this.history.past.length` and use `slice()` to keep only the most recent N entries, preventing memory growth during long editing sessions.

### How do I check if undo or redo actions are available in the UI?

Create computed properties that evaluate the length of the history arrays. Check `store.history.past.length > 0` to determine if undo is available, and `store.history.future.length > 0` for redo availability. Bind these booleans to the `disabled` attributes of your undo and redo buttons to provide clear visual feedback to users.