# How to Add Words to Favorites Using the TypeWords Pinia Store

> Learn how to add words to favorites using the TypeWords Pinia store. Import the composable and use addFavorite or toggleFavorite actions to easily manage your favorite words.

- Repository: [Zyronon/TypeWords](https://github.com/zyronon/TypeWords)
- Tags: how-to-guide
- Published: 2026-09-03

---

**To add words to favorites in TypeWords, import the `usePracticeStore` composable from `@/app/core/stores/practice` and call the `addFavorite(word)` or `toggleFavorite(word)` action, which automatically persists changes to IndexedDB.**

The TypeWords application manages user favorites through a dedicated **Pinia store** located at [`app/core/stores/practice.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/stores/practice.ts). This store provides a reactive `favorites` array and a set of actions that handle adding, removing, and toggling words—all with automatic persistence across browser sessions.

## Understanding the Practice Store Structure

The practice store is defined using Pinia's `defineStore('practice', …)` and exposes a **favorites** field in its state. According to the TypeWords source code, this field typically stores full word objects or their IDs depending on the implementation.

The store implements three primary actions for favorite management:

- **`addFavorite(word)`** — Adds a word to the favorites array if it isn't already present
- **`removeFavorite(word)`** — Removes a word from the favorites array
- **`toggleFavorite(word)`** — Switches a word's favorite status (adds if absent, removes if present)

## Accessing the Store in Components

Before calling any favorite action, you must import and instantiate the store. The standard pattern across the TypeWords codebase uses a composable-style import:

```ts
import { usePracticeStore } from '@/app/core/stores/practice'

// Inside a Vue component or another composable
const practiceStore = usePracticeStore()

```

Once instantiated, the store provides full access to the `favorites` reactive state and all associated actions.

## Adding a Word to Favorites: Basic Implementation

The simplest approach calls `addFavorite()` directly with a word object. This pattern appears throughout TypeWords components that implement favorite buttons:

```ts
<script setup lang="ts">
import { ref } from 'vue'
import { usePracticeStore } from '@/app/core/stores/practice'

const practice = usePracticeStore()
const currentWord = ref<Word | null>(null)

function onAddToFavorites() {
  if (currentWord.value) {
    practice.addFavorite(currentWord.value)
  }
}
</script>

<template>
  <button @click="onAddToFavorites">★ Add to Favorites</button>
</template>

```

When `addFavorite()` executes, it performs three operations:

1. Checks if the word already exists in `this.favorites` (prevents duplicates)
2. Pushes the word onto the array
3. Calls `saveFavorites()` to persist to IndexedDB

## Toggle Pattern for Favorite Buttons

Most TypeWords UI elements use **toggle behavior** rather than separate add/remove buttons. The `toggleFavorite()` action handles this logic internally:

```ts
import { usePracticeStore } from '@/app/core/stores/practice'
import type { Word } from '@/app/core/types'

export function useFavoriteWord(word: Word) {
  const practice = usePracticeStore()

  const isFavorited = computed(() =>
    practice.favorites.some(f => f.id === word.id)
  )

  function toggle() {
    practice.toggleFavorite(word)
  }

  return { isFavorited, toggle }
}

```

This composable pattern, common in `app/core/composables/practice-words/`, returns:

- A **reactive boolean** indicating favorite status
- A **toggle function** that delegates to the store action

## How Persistence Works: IndexedDB Integration

The TypeWords store persists favorites automatically through **idb-keyval** helpers defined in [`app/core/utils.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/utils.ts). The persistence flow works as follows:

```ts
// Simplified from app/core/stores/practice.ts
actions: {
  async init() {
    const stored = await get(FAVORITES_KEY)
    if (stored) this.favorites = JSON.parse(stored)
  },
  
  async saveFavorites() {
    await set(FAVORITES_KEY, JSON.stringify(this.favorites))
  },
  
  addFavorite(word) {
    if (!this.favorites.some(f => f.id === word.id)) {
      this.favorites.push(word)
      this.saveFavorites()  // Immediate persistence
    }
  },
  
  removeFavorite(word) {
    const index = this.favorites.findIndex(f => f.id === word.id)
    if (index > -1) {
      this.favorites.splice(index, 1)
      this.saveFavorites()  // Immediate persistence
    }
  },
  
  toggleFavorite(word) {
    const isFav = this.favorites.some(f => f.id === word.id)
    isFav ? this.removeFavorite(word) : this.addFavorite(word)
  }
}

```

The `FAVORITES_KEY` constant identifies the IndexedDB entry, ensuring favorites survive page reloads, browser restarts, and cache clears.

## Integration with Practice Flow Targets

The TypeWords practice system supports targeting specific word sets, including favorites. The `target` field in the practice flow configuration accepts `'favorite'` as a valid value, as defined in:

- [`app/core/composables/practice-words/practice-flow-types.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/composables/practice-words/practice-flow-types.ts) — TypeScript interfaces for flow configuration
- [`app/core/composables/practice-words/practice-flow-runtime.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/composables/practice-words/practice-flow-runtime.ts) — Runtime validation and execution

When `target: 'favorite'` is specified, the practice engine filters available words against the store's `favorites` array.

## Summary

- Import `usePracticeStore` from `@/app/core/stores/practice` to access favorite functionality
- Call **`addFavorite(word)`** to add, **`removeFavorite(word)`** to remove, or **`toggleFavorite(word)`** to switch status
- All changes auto-persist to **IndexedDB** via `saveFavorites()`—no manual storage handling required
- The **`favorites`** array is fully reactive—components update automatically when favorite status changes
- Practice flows can target favorites exclusively using `target: 'favorite'`

## Frequently Asked Questions

### How do I check if a word is already favorited?

Access the `favorites` array from the store and check for the word's ID. The common pattern uses `computed()` for reactivity: `computed(() => practice.favorites.some(f => f.id === word.id))`.

### Where are favorites stored between sessions?

Favorites persist to **IndexedDB** using the `idb-keyval` library. The `saveFavorites()` action in [`app/core/stores/practice.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/stores/practice.ts) handles serialization and storage automatically whenever favorites change.

### Can I use toggleFavorite for a star icon button?

Yes—`toggleFavorite()` is the recommended approach for star toggles. It internally checks the current state and calls either `addFavorite()` or `removeFavorite()`, simplifying UI component logic.

### What happens if I add the same word twice?

The `addFavorite()` action prevents duplicates by checking `this.favorites.some(f => f.id === word.id)` before pushing. Duplicate calls have no effect and don't trigger unnecessary persistence operations.