npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@tanstack/react-optimistic

v0.0.3

Published

React hooks for optimistic updates

Readme

@TanStack/react-optimistic

React hooks and utilities for creating fast optimistic updates with flexible backend support that pairs seamlessly with sync engines (like ElectricSQL).

Installation

pnpm add @TanStack/react-optimistic

Overview

@TanStack/react-optimistic provides React-specific hooks and utilities for managing data synchronization between your frontend application and backend services. It offers:

  • Optimistic Updates: Apply changes instantly in the UI while syncing in the background
  • Flexible Backend Support: Works with any backend or sync engine
  • Immutable Snapshots: Create immutable snapshots of updates that can be persisted and rolled back
  • React Integration: Seamless integration with React components and state management

React Hooks

useCollection

The primary hook for interacting with collections in React components.

const { data, insert, update, delete: deleteFn } = useCollection({
  id: 'todos',
  sync: { /* sync configuration */ },
  mutationFn: { /* mutation functions */ },
  schema: /* optional schema */
});

Returns:

  • data: An array of all items in the collection
  • state: A Map containing all items in the collection with their internal keys
  • insert: Function to add new items to the collection
  • update: Function to modify existing items
  • delete: Function to remove items from the collection

preloadCollection

Preloads data for a collection before rendering components.

await preloadCollection({
  id: 'todos',
  sync: { /* sync configuration */ },
  mutationFn: { /* mutation functions */ },
  schema: /* optional schema */
});

Features:

  1. Returns a promise that resolves when the first sync commit is complete
  2. Shares the same collection instance with useCollection
  3. Handles already-loaded collections by returning immediately
  4. Avoids duplicate initialization when called multiple times with the same ID

Data Operations

Insert

// Insert a single item
insert({ text: "Buy groceries", completed: false })

// Insert multiple items
insert([
  { text: "Buy groceries", completed: false },
  { text: "Walk dog", completed: false },
])

// Insert with custom key
insert({ text: "Buy groceries" }, { key: "grocery-task" })

Update

We use a proxy to capture updates as immutable draft optimistic updates.

// Update a single item
update(todo, (draft) => {
  draft.completed = true
})

// Update multiple items
update([todo1, todo2], (drafts) => {
  drafts.forEach((draft) => {
    draft.completed = true
  })
})

// Update with metadata
update(todo, { metadata: { reason: "user update" } }, (draft) => {
  draft.text = "Updated text"
})

Delete

// Delete a single item
delete todo

// Delete multiple items
delete [todo1, todo2]

// Delete with metadata
delete (todo, { metadata: { reason: "completed" } })

Implementing Backend Integration with ElectricSQL

The mutationFn property is where you define how your application interacts with your backend. Here's a comprehensive example of integrating with ElectricSQL:

import { useCollection } from "@TanStack/react-optimistic"
import { createElectricSync } from "@TanStack/optimistic/electric"

// Create a collection configuration for todos
const todosConfig = {
  id: "todos",
  // Create an ElectricSQL sync configuration
  sync: createElectricSync(
    {
      // ShapeStream options
      url: `http://localhost:3000/v1/shape`,
      params: {
        table: "todos",
      },
    },
    {
      // Primary key for the todos table
      primaryKey: ['id'],
    }
  ),
}

// Use the collection in a component
function TodoList() {
  const { data, insert, update, delete: deleteFn } = useCollection(todosConfig)

  // Create a mutation for handling all todo operations
  const todoMutation = useOptimisticMutation({
    mutationFn: async ({ transaction }) => {
      const payload = transaction.mutations.map(m => {
        const { collection, ...payload } = m
        return payload
      })

      const response = await fetch(`http://localhost:3001/api/mutations`, {
        method: `POST`,
        headers: {
          "Content-Type": `application/json`,
        },
        body: JSON.stringify(payload),
      })

      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`)
      }

      const result = await response.json()
      await transaction.mutations[0].collection.config.sync.awaitTxid(result.txid)
    }
  })

  const addTodo = () => {
    todoMutation.mutate(() => {
      insert({ title: 'New todo', completed: false })
    })
  }

  const toggleTodo = (todo) => {
    todoMutation.mutate(() => {
      update(todo, (draft) => {
        draft.completed = !draft.completed
      })
    })
  }

  const removeTodo = (todo) => {
    todoMutation.mutate(() => {
      deleteFn(todo)
    })
  }

  return (
    <div>
      <button
        onClick={addTodo}
        disabled={todoMutation.isPending}
      >
        {todoMutation.isPending ? 'Saving...' : 'Add Todo'}
      </button>
      <ul>
        {data.map(todo => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo)}
              disabled={todoMutation.isPending}
            />
            {todo.title}
            <button
              onClick={() => removeTodo(todo)}
              disabled={todoMutation.isPending}
            >
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  )
}