Good — the advisor confirms exactly two scrub points and resolves the ambiguity on the client category. Applying now.


TLDR: SWR's revalidateOnFocus, revalidateOnReconnect, and revalidateIfStale are all true by default. On a drag-and-drop board, that's lethal. Wrap your app in a <SWRConfig> and turn them off.

The Board We Were Building

Our internal KOL CRM is a content calendar board for an ecommerce client — six months of webinars laid out in a calendar grid, with KOLs (key opinion leaders, basically featured guests), topics, and traffic sources dragged onto each event as piece tiles.

It looks fantastic when it's not jittering.

The Wall We Hit

The board worked great… until it didn't.

Cards would overflow their grid cells mid-scroll.

The whole layout would re-render while you were dragging something.

Tab away, come back — the board flashed, data reloaded, your scroll position was gone.

We had some serious jank and it was getting worse with every feature we added.

What We Tried First (Wrong)

My first instinct was CSS.

I ripped out translate3d transforms.

I pulled backdrop-filter and GPU composite layers — those are infamous for creating weird overflow artifacts.

I added overflow-hidden to every container I could find.

Things got slightly better, then regressed again. The artifacts kept coming back after mutations. I kept chasing the wrong thing.

The Actual Problem

It was SWR (the React data-fetching library) the whole time.

SWR, by default, revalidates your data in three situations you might not think about:

  • When the user focuses the tab after being away
  • When the browser reconnects to the network
  • When cached data is considered stale

On a content calendar board with a dozen useSWR hooks in the tree, "revalidate" means: re-fetch all your webinar data, trigger a React re-render, and repaint the board from scratch.

In the middle of a drag. In the middle of a scroll. Every single time you Alt-Tab back to the app.

The scroll artifacts weren't CSS. The board was being re-drawn under our hands.

The Fix That Worked

One SWRProvider component, wrapped around the app:

<SWRConfig
  value={{
    fetcher,
    revalidateOnFocus: false,
    revalidateOnReconnect: false,
    revalidateIfStale: false,
    dedupingInterval: 5000,
    errorRetryCount: 2,
    keepPreviousData: true,
  }}
>
  {children}
</SWRConfig>

That's it. That's the whole fix.

keepPreviousData: true was a bonus — navigating between months no longer flashes an empty skeleton while the new range loads. The old data stays visible until fresh data arrives.

For mutations, we leaned on optimistic cache updates via useSWRConfig's mutate — so dragging a piece onto a date updates the UI immediately, with no post-save flash waiting for the server to confirm.

And we added a manual refresh button to the board header. That's the trade-off: you give up automatic freshness and hand the user a button. For a board where YOU are the one making all the changes, that's the right call. You don't need the server telling you what you just did.

Why This Matters

SWR's defaults are GREAT for a typical dashboard — prices, analytics, live data where staleness actually matters. But a drag-and-drop board is different. The user IS the source of truth. The only time the board should re-fetch is when you ask it to.

If you're building anything interactive — kanban boards, calendars, editors, anything with drag-and-drop — your first SWR task is to turn off the automatic revalidation. Don't wait until you're chasing phantom CSS bugs for two days like I did.

The lesson I'm keeping: when the UI jitters on a data-heavy board, check the data layer before you blame CSS. Background re-renders look exactly like GPU/compositing artifacts. They're not. They're your fetcher doing its job too enthusiastically.

P.S. The dedupingInterval default in SWR is 2 seconds. If you have multiple components on the same page calling useSWR with the same key, you can accidentally fire the same fetch many times in a short burst. We settled on 5000 (5s). Tune it for your use case — but know the knob exists.