---
title: "Page fetches its data in `useEffect` (lint/useeffect-fetch)"
description: "lint/useeffect-fetch: Fetching on mount in a client page renders an empty shell first, then waterfalls the request after JS downloads and hydrates: slower…"
canonical: https://void-design.vercel.app/rules/lint/useeffect-fetch
lastModified: 2026-09-16
---

# Page fetches its data in `useEffect`

`lint/useeffect-fetch` · severity **warn** · category Lint · detected by `void lint` and `void audit`

## Why it matters

Fetching on mount in a client page renders an empty shell first, then waterfalls the request after JS downloads and hydrates: slower LCP, layout shift when data arrives, and crawlers see no content.

## How to fix it

Fetch in the Server Component (`async function Page()`), pass data down as props, and stream slow parts with `<Suspense>`. For client-side interactivity use a cache (SWR/React Query) seeded with server data.

## Example

```tsx
export default async function Page() {
  const posts = await getPosts()
  return <PostList posts={posts} />
}
```

## References

- https://nextjs.org/docs/app/getting-started/fetching-data

## More lint rules

`void lint` reports 67 rules in this category. Static source checks with no browser: Tailwind v4 silent failures, Next.js 16 API traps, React render-body bugs, accessibility markup, SEO files and motion hygiene.

- `raf-without-cancel` `requestAnimationFrame` loop in an effect without `cancelAnimationFrame` — [lint/raf-without-cancel](https://void-design.vercel.app/rules/lint/raf-without-cancel)
- `listener-without-cleanup` Event listener or interval added in an effect without cleanup — [lint/listener-without-cleanup](https://void-design.vercel.app/rules/lint/listener-without-cleanup)
- `scroll-listener-nonpassive` `wheel` / `touchstart` / `touchmove` listener without `{ passive: true }` — [lint/scroll-listener-nonpassive](https://void-design.vercel.app/rules/lint/scroll-listener-nonpassive)
- `unload-listener` `unload` event listener — [lint/unload-listener](https://void-design.vercel.app/rules/lint/unload-listener)
- `div-button` Clickable `<div>`/`<span>` without button semantics — [lint/div-button](https://void-design.vercel.app/rules/lint/div-button)
- `dangerously-set-jsonld-unescaped` JSON-LD injected with `JSON.stringify` without escaping `<` — [lint/dangerously-set-jsonld-unescaped](https://void-design.vercel.app/rules/lint/dangerously-set-jsonld-unescaped)
- `transition-all` `transition: all` / `transition-all` — [lint/transition-all](https://void-design.vercel.app/rules/lint/transition-all)
- `animate-layout-prop` Animation of layout properties (width/height/top/left/margin/padding) — [lint/animate-layout-prop](https://void-design.vercel.app/rules/lint/animate-layout-prop)

Detected by `void lint` and `void audit`. Explain it in a terminal: `void rules lint/useeffect-fetch`
