---
title: "Optimistic list"
description: "Add and toggle items in a Convex list without waiting for the server round-trip."
canonical_url: "https://nuxt-convex-module.dev/recipes/optimistic-list"
---
# Optimistic list

> Add and toggle items in a Convex list without waiting for the server round-trip.

Chain `.withOptimisticUpdate()` onto a mutation: the handler writes the expected result into the local query store, the UI renders it at once, and the real result replaces it when the server responds. Try it — the checkboxes flip before the mutation returns:

<playground-tasks>



</playground-tasks>

```vue [components/TaskList.vue]
<script setup lang="ts">
import { api } from '#convex/api'
import type { Id } from '#convex/dataModel'

const { results, status, loadMore } = usePaginatedQuery(
  api.tasks.listPaginated,
  {},
  { initialNumItems: 10 },
)

const addTask = useMutation(api.tasks.add).withOptimisticUpdate((localStore, args) => {
  insertAtTop({
    paginatedQuery: api.tasks.listPaginated,
    localQueryStore: localStore,
    item: { _id: crypto.randomUUID() as Id<'tasks'>, _creationTime: Date.now(), text: args.text, completed: false },
  })
})

const toggleTask = useMutation(api.tasks.toggle).withOptimisticUpdate((localStore, args) => {
  optimisticallyUpdateValueInPaginatedQuery(localStore, api.tasks.listPaginated, {}, task =>
    task._id === args.id ? { ...task, completed: !task.completed } : task)
})

const text = ref('')
</script>

<template>
  <form @submit.prevent="addTask({ text }); text = ''">
    <input v-model="text" placeholder="New task">
  </form>
  <label v-for="task in results" :key="task._id">
    <input type="checkbox" :checked="task.completed" @change="toggleTask({ id: task._id })">
    {{ task.text }}
  </label>
  <button :disabled="status !== 'CanLoadMore'" @click="loadMore(10)">Load more</button>
</template>
```

- The handler must be synchronous; call `.withOptimisticUpdate()` once per `useMutation`, not per click.
- For a plain (non-paginated) query, read and write it with `localStore.getQuery` / `setQuery` — see [Optimistic updates](https://nuxt-convex-module.dev/guide/mutations-and-actions#optimistic-updates).
- If the mutation throws, the optimistic write is rolled back on its own.

The four paginated helpers (`insertAtTop`, `insertAtBottomIfLoaded`, `insertAtPosition`, `optimisticallyUpdateValueInPaginatedQuery`): [Pagination](https://nuxt-convex-module.dev/guide/pagination#optimistic-updates-over-pages).


## Sitemap

See the full [sitemap](https://nuxt-convex-module.dev/sitemap.md) for all pages.
