#4180·swr

SWR re-renders the whole paginated data array when optimisticData and data functions return an array

Author: tandetatCreated Nov 13, 2025Updated Aug 1, 2026

Bug report

Description / Observed Behavior

When using mutate() with both optimisticData and an async data function that return structurally identical data (but different references), SWR triggers two renders even though the data content is the same. This causes mutations to paginated data to re-render the whole array twice.

Observed in useSWRInfinite:

typescript
const { data, mutate } = useSWRInfinite(getKey, fetcher);

// This causes TWO renders even if server returns identical data
 mutate(
   async (current) => {
     const result = await api.addItem(item);
     return [...result]; // New reference #1
   },
   {
     optimisticData: (current) => [...current, item], // New reference #2
     revalidate: false
   }
);

Result:

  • First render with optimisticData array
  • Second render with final data array (even though contents are identical)

Expected Behavior

SWR should use compare() function in mutation paths. When the final data from a mutation is structurally identical to the current cached data (including optimistic data), SWR should return the existing reference to avoid a cache update and match the revalidation.

Repro Steps / Code Example

typescript
import useSWRInfinite from 'swr/infinite';

  function MyComponent() {
    const { data, mutate } = useSWRInfinite(
      (index) => `/api/items?page=${index}`,
      fetcher
    );

    const addItem = async (item: Item) => {
      // BUG: This causes two renders even if API returns the same item
      await mutate(
        async (currentPages) => {
          const newItem = await fetch('/api/items', {
            method: 'POST',
            body: JSON.stringify(item)
          }).then(r => r.json());

          // Optimistic item and server item have same content
          // but different references
          return currentPages.map((page, i) =>
            i === 0
              ? [newItem, ...page]  // New reference
              : page
          );
        },
        {
          optimisticData: (currentPages) =>
            currentPages.map((page, i) =>
              i === 0
                ? [item, ...page]  // New reference
                : page
            ),
          revalidate: false
        }
      );
    };

    // Renders twice even though optimistic and final data are identical
    return <div>{data?.[0]?.length} items</div>;
  }

Additional Context

SWR version: 2.3.6