#useLayoutEffect
useLayoutEffect!!
March 31, 2025 at 2:22 PM
⚛️ Day 33 #100DaysOfCode #React useLayoutEffect
React’s render cycle 👇
useLayoutEffect → paint →useEffect

🧠 useEffect:
Non-visual side effects, things the user won't see: data, timers, logging

🎨 useLayoutEffect:
Visual sync, things that affect what’s painted: size, position, layout
November 12, 2025 at 2:31 AM
you have no idea how excited I am to use these 3 lines of code

useLayoutEffect(() => {
ref.current.getBoundingClientRect()
})

we can remove so much jank, it brings a tear to my eye 🥲
October 25, 2024 at 5:19 AM
Yeah in the new arch you can use useLayoutEffect to read layout synchronously like on web
November 30, 2024 at 7:15 PM
difference between:

~ useLayoutEffect and useEffect

August 2, 2026 at 8:45 PM
Finally reading about React’s Activity API and it’s very promising. The docs do make me long for a stronger intuition for useEffect vs useLayoutEffect though.
September 20, 2025 at 8:01 AM
🙌 I got my first contribution into React!

It's a small one that adds a dev-only warning if you forget to pass a callback to a use*Effect function. Which basically never happens if you're using TypeScript. Still, cool to contribute!

github.com/facebook/rea...

Thanks @no.lol for the quick reviews!
Added dev-only warning for null/undefined create in use*Effect by JoshuaKGoldberg · Pull Request #32355 · facebook/react
Summary Fixes #32354. Re-creation of #15197: adds a dev-only warning if create == null to the three use*Effect functions: useEffect useInsertionEffect useLayoutEffect Updates the warning to match...
github.com
February 13, 2025 at 2:01 PM
Also useLayoutEffect works now, not sure if that worked in paper or if it’s fabric only
May 5, 2025 at 11:41 PM
🚀 React Hooks: useEffect vs useLayoutEffect

useEffect:
• Runs after paint
• Async, non-blocking
• Use for: data fetching, subscriptions
• Default choice for most cases

useLayoutEffect:
• Runs before paint
• Sync, blocking
• Use for: DOM measurements, preventing flickers
• Performance sensitive
December 28, 2024 at 6:57 PM
⚛️ Day 34 #100DaysOfCode #React has 2 phases

1️⃣ Render Phase = calculate UI
React runs your component + pure hooks
Builds + diffs the virtual DOM
❌ No DOM, no effects

2️⃣ Commit Phase = apply UI
React updates the DOM, then runs:
➡️ useLayoutEffect (before paint)
➡️ useEffect (after paint)

#frontend
November 13, 2025 at 5:58 AM
Doing a little side project now and I can't figure it out - how'd you all use React Native before proper useLayoutEffect?
December 9, 2024 at 10:58 PM
react-native-volume-manager is much more than a volume listener. Plays super nice with @expo.dev expo-video (not expo-av) and let you enable and disable the audio session and category, informs the OS in a way so your music can resume automatically, too. (AppState, useLayoutEffect etc)
December 15, 2024 at 4:50 PM
Unlocking React's potential with hooks? 🤔 While useEffect is a go-to for async tasks, don't overlook useLayoutEffect! Perfect for precise DOM updates, ensuring snappy, flicker-free UIs. Master its use and elevate your performance game! 🚀 #WebDevelopment #ReactHooks
February 15, 2025 at 3:44 PM
A useLayoutEffect is probably part of the equation. I understand how we measure the element, but not the length of the text. The useLayoutEffect would measure both, then determine if the text should be marqueed or not

(useLayoutEffect runs before render)
February 16, 2026 at 8:35 AM
What is wrong with this #react code?
January 1, 2025 at 12:00 PM
React Hooks Face-Off! ⚡
useEffect vs useLayoutEffect — Do you know when to use which?

Perfect for JavaScript & React devs aiming to ace their next frontend interview in 2025!

Save this post — your future self will thank you!

#ReactHooks #useEffect #useLayoutEffect #ReactJS #FrontendDeveloper
August 20, 2025 at 10:34 AM
Welcome Back, React Hooks Made Simple: The Complete Guide, Part-2
In [Part 1] https://dev.to/abrar_galib_5c0cf41ad3a3e/react-hooks-made-simple-the-complete-guide-part-1-2jln we covered the core Hooks (useState, useEffect, useContext), the additional Hooks (useRef, useMemo, useCallback, useReducer) and the advanced ones (useLayoutEffect, useImperativeHandle, useId). If you haven't read it yet, start there, because everything in this part builds on it. In Part 2: * Two Hooks that keep your app smooth when an update is heavy * A few more advanced Hooks for outside data and library authors * The new Hooks that came with React 19 and later * The Hooks that belong to Next.js * Custom Hooks, common mistakes, and a cheatsheet you can keep open while you code Everything below works on React 19. A few things need a newer minor version, and I point them out as we go: `useEffectEvent` needs React 19.2, and `ViewTransition`, Fragment Refs and `use(browser())` need React 19.3, which is the current stable release. ## **Table of Contents** * Performance Hooks * useTransition * useDeferredValue * More Advanced Hooks * useSyncExternalStore * useInsertionEffect * useDebugValue * New Hooks in React 19 and Later * useActionState * useFormStatus * useOptimistic * use * useEffectEvent * Bonus: ViewTransition and Fragment Refs * Next.js Hooks * useRouter * usePathname * useSearchParams * useParams * useLinkStatus * useServerInsertedHTML * Custom Hooks * Common Mistakes to Avoid * Cheatsheet * Conclusion ## **Performance Hooks** Sometimes an update is heavy: a huge list, a slow tab, a big search result. If React does all the work at once, the page freezes. Typing feels laggy and buttons stop responding. These two Hooks let you tell React: "this update is not urgent, so do the important things first." **What is an urgent update?** Anything the user expects to see instantly, like typing in a box or clicking a button. Showing the search results or opening a heavy tab can wait a moment. React can handle the urgent update first and finish the slow one in the background. ### useTransition useTransition lets you mark a state update as low priority. It gives you two things: `isPending`, which is `true` while the slow update is happening, and `startTransition`, a function you wrap around the state update. 'use client'; import { useState, useTransition } from 'react'; function About() { return <p>Hi, I write about React.</p>; } function SlowPosts() { // Pretend this tab is heavy: 300 rows, and each row takes a little time const rows = []; for (let i = 0; i < 300; i++) { rows.push(<SlowRow key={i} number={i + 1} />); } return <ul>{rows}</ul>; } function SlowRow({ number }) { const start = performance.now(); while (performance.now() - start < 2) { // do nothing for 2 ms on purpose } return <li>Post #{number}</li>; } export default function TabContainer() { const [tab, setTab] = useState('about'); const [isPending, startTransition] = useTransition(); function selectTab(nextTab) { // This update can wait, so the page stays clickable startTransition(() => { setTab(nextTab); }); } return ( <> <button onClick={() => selectTab('about')}>About</button> <button onClick={() => selectTab('posts')}>Posts</button> {isPending && <p>Loading...</p>} {tab === 'about' ? <About /> : <SlowPosts />} </> ); } Click "Posts" and the old tab stays on screen while React prepares the new one in the background. You can click "About" again at any time and React will drop the slow work and switch back. Without `startTransition`, the whole page would freeze for a moment. **Note:** In React 19 you can even pass an `async` function to `startTransition`, and `isPending` stays `true` until it finishes. This is the base for the form Hooks later in this guide. ### useDeferredValue useDeferredValue gives you a copy of a value that intentionally "lags behind". While the user keeps typing, your input stays fast, and the heavy part of the screen catches up a moment later. Use it when you can't wrap the state setter yourself, for example when the value arrives as a prop. 'use client'; import { memo, useDeferredValue, useState } from 'react'; const Results = memo(function Results({ items, query }) { const matches = items.filter((item) => item.toLowerCase().includes(query.toLowerCase()) ); return ( <ul> {matches.map((item) => ( <li key={item}>{item}</li> ))} </ul> ); }); export default function Search({ items }) { const [query, setQuery] = useState(''); const deferredQuery = useDeferredValue(query); const isStale = query !== deferredQuery; return ( <> <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search..." /> <div style={{ opacity: isStale ? 0.5 : 1 }}> <Results items={items} query={deferredQuery} /> </div> </> ); } Two things to remember: * You must wrap the slow component in `memo`. If you don't, it re-renders on every keystroke anyway and you save nothing. * `query !== deferredQuery` tells you the list is still catching up. Here we use it to dim the old results. React 19 also lets you pass a starting value as the second argument, like `useDeferredValue(query, '')`. **useTransition or useDeferredValue?** | useTransition | useDeferredValue ---|---|--- What it wraps | The code that sets state | The value itself Use it when | You own the state setter | The value comes from a prop or a Hook You get | `isPending` flag | A lagging copy of the value **Note:** Don't wrap the `setState` of a text input in `startTransition`. The text in the box must update instantly, so the input state has to stay urgent. Defer the slow part instead, like we did above. ## **More Advanced Hooks** You will use these three much less often than the ones above. Still, you should know what they are, because you'll see them in libraries and in other people's code. ### useSyncExternalStore useSyncExternalStore connects React to data that lives outside of React and changes on its own, like the browser's online status, the window size, or a global store. **What is an external store?** It's any data source that React doesn't own. React can't know when it changes, so you have to tell React how to listen to it. Libraries like Redux and Zustand use this Hook under the hood. It takes three functions: * `subscribe`: starts listening for changes and returns a cleanup function * `getSnapshot`: reads the current value * `getServerSnapshot`: gives a starting value for server rendering, so the server HTML matches the browser HTML during hydration 'use client'; import { useSyncExternalStore } from 'react'; function subscribe(callback) { window.addEventListener('online', callback); window.addEventListener('offline', callback); return () => { window.removeEventListener('online', callback); window.removeEventListener('offline', callback); }; } function useOnlineStatus() { return useSyncExternalStore( subscribe, () => navigator.onLine, // getSnapshot: read the browser value () => true // getServerSnapshot: what the server should assume ); } export default function StatusBar() { const isOnline = useOnlineStatus(); return <p>{isOnline ? 'Online' : 'Offline'}</p>; } Why not just use `useEffect` and `useState`? Because this Hook also prevents a bug called tearing, where two parts of the screen show different values of the same data during a render. Two rules to remember: * Define `subscribe` outside your component. If it's created inside, React will unsubscribe and subscribe again on every render. * `getSnapshot` must return the same value when nothing changed. If it returns a brand-new object every time, you will get an infinite loop. In Next.js, `getServerSnapshot` is required if the component renders on the server. ### useInsertionEffect This one is built for people who write CSS-in-JS libraries. It runs before React changes the DOM, so a library can safely inject `<style>` tags at the right moment. If you build normal apps, you will probably never need it. Here is the order in which the three effect Hooks run: 1. `useInsertionEffect` runs before React updates the DOM 2. `useLayoutEffect` runs after the DOM updates, before the browser paints 3. `useEffect` runs after the browser paints 'use client'; import { useInsertionEffect } from 'react'; export default function Highlight({ children }) { useInsertionEffect(() => { const style = document.createElement('style'); style.textContent = '.highlight { background: yellow; }'; document.head.appendChild(style); return () => document.head.removeChild(style); }, []); return <mark className="highlight">{children}</mark>; } Libraries like styled-components and Emotion already do this for you, so just use them. ### useDebugValue A tiny Hook that adds a label next to your custom Hook inside React DevTools. It only helps with debugging, and it only works inside custom Hooks. import { useDebugValue, useSyncExternalStore } from 'react'; // "subscribe" is the same function from the previous example function useOnlineStatus() { const isOnline = useSyncExternalStore( subscribe, () => navigator.onLine, () => true ); useDebugValue(isOnline ? 'Online' : 'Offline'); return isOnline; } Open DevTools, find the component that uses `useOnlineStatus`, and you will see the `Online` or `Offline` label right next to it. ## **New Hooks in React 19 and Later** React 19 was built around one idea: **Actions**. A few new Hooks make Actions easy to use, especially with forms. Then React 19.2 and 19.3 added a few more useful things on top. **What is an Action?** An Action is a function that changes something, usually by talking to a server. Submitting a form is the classic example. React runs it inside a transition, so it can track a pending state for you, and it updates the screen when the work is done. ### useActionState useActionState runs a function when a form is submitted and keeps track of the result. You get the latest result, a function to put on your form, and a `isPending` flag. 'use client'; import { useActionState } from 'react'; async function subscribe(previousState, formData) { const email = formData.get('email'); // Pretend we are talking to a server await new Promise((resolve) => setTimeout(resolve, 1000)); if (!email.includes('@')) { return { message: 'Please enter a valid email.' }; } return { message: `Thanks! We will write to ${email}.` }; } export default function NewsletterForm() { const [state, formAction, isPending] = useActionState(subscribe, { message: '', }); return ( <form action={formAction}> <input name="email" type="text" placeholder="you@example.com" /> <button type="submit" disabled={isPending}> {isPending ? 'Sending...' : 'Subscribe'} </button> {state.message && <p>{state.message}</p>} </form> ); } How it works: 1. You pass in your function and a starting state. 2. You put `formAction` on the form's `action` prop. 3. When the form is submitted, React calls your function with the previous state and the form data. 4. Whatever your function returns becomes the new `state`. Notice that we don't need `onSubmit`, `preventDefault` or a `useState` for every input. React reads the form data for us. **Note:** After the action finishes, React resets the form's fields. That's normal behavior in React 19. In Next.js you can pass a Server Action instead. Put it in its own file with `'use server'` at the top, then import it: // app/actions.js 'use server'; export async function subscribe(previousState, formData) { // save to your database here return { message: 'Thanks for subscribing!' }; } You might see the old name `useFormState` in older tutorials. It was renamed to `useActionState`. ### useFormStatus useFormStatus tells a component whether the form it sits inside is currently submitting. It's perfect for a submit button. You import it from `react-dom`, not from `react`. 'use client'; import { useFormStatus } from 'react-dom'; function SubmitButton() { const { pending } = useFormStatus(); return ( <button type="submit" disabled={pending}> {pending ? 'Saving...' : 'Save'} </button> ); } async function saveProfile(formData) { await new Promise((resolve) => setTimeout(resolve, 1000)); console.log('Saved:', formData.get('name')); } export default function ProfileForm() { return ( <form action={saveProfile}> <input name="name" placeholder="Your name" /> <SubmitButton /> </form> ); } **Note:** `useFormStatus` only works in a component that is rendered _inside_ a `<form>`. If you call it in the same component that renders the `<form>`, `pending` will always be `false`. That's why the button is its own component here. The nice part is that you can reuse `SubmitButton` in every form in your app. ### useOptimistic **What is Optimistic UI?** When you like a post on a social app, the heart turns red right away. It doesn't wait for the server. The app is being optimistic: it assumes the server will say yes, shows the result immediately, and fixes things later if something goes wrong. useOptimistic gives you a temporary value that lasts only while an Action is running. When the Action finishes, React switches back to the real state. 'use client'; import { useOptimistic, useState, useTransition } from 'react'; async function saveLike() { // Pretend this is a slow server call await new Promise((resolve) => setTimeout(resolve, 1500)); } export default function LikeButton() { const [likes, setLikes] = useState(0); const [, startTransition] = useTransition(); const [optimisticLikes, addOptimisticLike] = useOptimistic( likes, (currentLikes, amount) => currentLikes + amount ); function handleClick() { startTransition(async () => { addOptimisticLike(1); // the screen updates instantly await saveLike(); // the real work takes 1.5 seconds setLikes((current) => current + 1); // now update the real state }); } return <button onClick={handleClick}>Likes: {optimisticLikes}</button>; } Click the button and the number goes up right away, even though the "server" takes 1.5 seconds. Once the Action is done, React drops the temporary value and shows the real one, which is the same number, so nothing jumps. If the save fails, the temporary value disappears and the real number is shown again. The setter (`addOptimisticLike`) must be called inside an Action, like inside `startTransition` or a form `action`. If you call it anywhere else, React will show you a warning. **Note:** `useOptimistic` is a React Hook. It's not specific to Next.js, even though it works great with Server Actions. ### use `use` lets you read a resource while rendering. It works with two things: a promise and a context. It looks like a Hook, but it has a special power: you can call it inside `if` statements and loops, and after an early return. Normal Hooks can't do that. The one place you can't use it is inside `try` / `catch`. **Reading a promise** The best pattern is to create the promise in a Server Component, pass it down, and wrap the client component in `Suspense`. // app/page.js (a Server Component) import { Suspense } from 'react'; import Comments from './comments'; export default function Page() { const commentsPromise = fetch( 'https://jsonplaceholder.typicode.com/comments?_limit=5' ).then((res) => res.json()); return ( <Suspense fallback={<p>Loading comments...</p>}> <Comments commentsPromise={commentsPromise} /> </Suspense> ); } // app/comments.js 'use client'; import { use } from 'react'; export default function Comments({ commentsPromise }) { const comments = use(commentsPromise); // waits here until the data arrives return ( <ul> {comments.map((comment) => ( <li key={comment.id}>{comment.name}</li> ))} </ul> ); } While the promise is loading, React shows the `Suspense` fallback. When it's ready, the component renders with the data. There is no `useEffect`, no loading state and no `isLoading` flag. **Note:** Don't create a new promise inside a Client Component while rendering, and then pass it to `use`. It would create a fresh promise on every render. Create it in a Server Component, or use a library that caches it. **Reading context, even after an`if`** import { use } from 'react'; import { ThemeContext } from './theme-context'; function Heading({ children }) { if (children == null) { return null; } // useContext can't be called here, but use can const theme = use(ThemeContext); return <h1 className={theme}>{children}</h1>; } **New in React 19.3:`use(browser())`** Some components can't make sensible HTML on the server, for example because they read the visitor's time zone. Calling `use(browser())` tells React to skip this component during server rendering and show the nearest `Suspense` fallback instead. Once the page loads in the browser, it renders normally. import { Suspense, use } from 'react'; import { browser } from 'react-dom'; function TimeZone() { use(browser()); const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone; return <p>{timeZone}</p>; } export default function App() { return ( <Suspense fallback="Loading..."> <TimeZone /> </Suspense> ); } Before 19.3, people did this with a `mounted` state and a `useEffect`. Now there is a proper way. **Note:** In recent Next.js versions, the `params` prop of a page is a promise. In a Client Component page you read it with `use`: 'use client'; import { use } from 'react'; export default function Page({ params }) { const { slug } = use(params); return <h1>{slug}</h1>; } ### useEffectEvent _Needs React 19.2 or newer._ Here is a problem you will meet sooner or later. Your effect needs to _read_ a value, but you don't want the effect to _re-run_ when that value changes. Imagine we log a page visit, and we also want to log how many items are in the cart: 'use client'; import { useEffect, useEffectEvent } from 'react'; function logVisit(url, numberOfItems) { console.log(`Visited ${url} with ${numberOfItems} items in the cart`); } export default function Page({ url, numberOfItems }) { // This function always sees the latest numberOfItems const onVisit = useEffectEvent((visitedUrl) => { logVisit(visitedUrl, numberOfItems); }); useEffect(() => { onVisit(url); }, [url]); // only url here. No numberOfItems, no onVisit. return <h1>Page: {url}</h1>; } If we had put `numberOfItems` in the dependency array, the visit would be logged again every time the cart changes, which is wrong. If we left it out without `useEffectEvent`, the effect would read an old value. `useEffectEvent` solves both problems. Rules to remember: * Only call an Effect Event from inside an effect. * Don't pass it to other components or Hooks. * Never put it in the dependency array. **Note:** You might see `useEvent` in older blog posts. That was the early experimental name, and it never shipped in stable React. The real name is `useEffectEvent`. ### Bonus: ViewTransition and Fragment Refs React 19.3 made two more features stable. Neither of them is a Hook, but you will start seeing them in newer codebases, so here is a quick look. **ViewTransition** `<ViewTransition>` animates elements when they enter, exit or change, using the browser's View Transition API. By default you get a smooth cross-fade. 'use client'; import { ViewTransition, useState, startTransition } from 'react'; export default function Toggle() { const [show, setShow] = useState(false); return ( <> <button onClick={() => { startTransition(() => { setShow((prev) => !prev); }); }} > {show ? 'Hide' : 'Show'} card </button> {show && ( <ViewTransition> <div className="card">Hello! I fade in and out.</div> </ViewTransition> )} </> ); } React picks the animation based on what happened: **enter** (added), **exit** (removed), **update** (children changed) or **share** (a named one moved from one place to another). The important rule: only updates marked as a Transition animate. That means state updates inside `startTransition`, a `Suspense` reveal, or an update from `useDeferredValue`. That's why we learned `useTransition` first. It currently works in the DOM only, not in React Native. **Fragment Refs** Before, you needed a wrapper `<div>` just to attach a ref to a group of elements. Now you can pass a ref straight to a `<Fragment>` and work with its DOM children as a group. 'use client'; import { Fragment, useRef } from 'react'; export default function Links() { const fragmentRef = useRef(null); return ( <> <button onClick={() => fragmentRef.current.focus()}> Focus the first link </button> <Fragment ref={fragmentRef}> <a href="#one">One</a> <a href="#two">Two</a> </Fragment> </> ); } The ref gives you a `FragmentInstance` with methods for events (`addEventListener`), focus (`focus`, `focusLast`, `blur`), observers (`observeUsing`) and scrolling (`scrollIntoView`). You must write `<Fragment>` in full. The short `<>` syntax can't take a ref. ## **Next.js Hooks** These Hooks don't come from React. They come from Next.js, and they work in the App Router (the `app` folder). Because they read browser and route information, they only work in **Client Components** , so put `'use client'` at the top of the file. **Note:** If you see `import { useRouter } from 'next/router'`, that's the old Pages Router. In the App Router, always import from `next/navigation`. ### useRouter useRouter lets you change pages from your code, for example after a login. For normal links, use the `<Link>` component instead. 'use client'; import { useRouter } from 'next/navigation'; export default function LoginButton() { const router = useRouter(); async function handleLogin() { // ...log the user in... router.push('/dashboard'); } return <button onClick={handleLogin}>Log in</button>; } The router object has these methods: * `router.push('/path')` goes to a new page and adds it to the history * `router.replace('/path')` goes to a new page without adding to the history * `router.back()` and `router.forward()` move through the history * `router.refresh()` reloads the current route's data * `router.prefetch('/path')` loads a page in the background before the user clicks ### usePathname usePathname returns the current URL path, like `/blog/my-post`. A very common use is highlighting the active link in a navbar. 'use client'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; const links = [ { href: '/', label: 'Home' }, { href: '/blog', label: 'Blog' }, ]; export default function NavBar() { const pathname = usePathname(); return ( <nav> {links.map((link) => ( <Link key={link.href} href={link.href} style={{ fontWeight: pathname === link.href ? 'bold' : 'normal' }} > {link.label} </Link> ))} </nav> ); } ### useSearchParams useSearchParams reads the query string, the part of the URL after the `?`, like `/products?sort=asc`. It's read-only. To change it, build a new query string and navigate to it. 'use client'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; export default function SortButtons() { const searchParams = useSearchParams(); const router = useRouter(); const pathname = usePathname(); const sort = searchParams.get('sort') ?? 'asc'; function changeSort(value) { const params = new URLSearchParams(searchParams.toString()); params.set('sort', value); router.push(`${pathname}?${params.toString()}`); } return ( <> <p>Sorted by: {sort}</p> <button onClick={() => changeSort('asc')}>Ascending</button> <button onClick={() => changeSort('desc')}>Descending</button> </> ); } Wrap the component that uses `useSearchParams` in a `Suspense` boundary. On a statically rendered page, the query string isn't known while building, so Next.js needs somewhere to show a fallback: // app/products/page.js import { Suspense } from 'react'; import SortButtons from './sort-buttons'; export default function Page() { return ( <Suspense fallback={<p>Loading...</p>}> <SortButtons /> </Suspense> ); } Keeping filters and sorting in the URL is a great habit. Users can refresh the page or share the link and see the same result. ### useParams useParams reads the dynamic parts of the URL. If your file is `app/blog/[slug]/page.js` and the user visits `/blog/react-hooks`, then `slug` is `react-hooks`. 'use client'; import { useParams } from 'next/navigation'; export default function PostTitle() { const { slug } = useParams(); return <h1>Reading: {slug}</h1>; } There is also `useSelectedLayoutSegment`, which tells a layout which child route is currently active. It's handy for tabs and sidebars. ### useLinkStatus useLinkStatus tells you if a `<Link>` is still loading its page. It's great for showing a small spinner right inside the link, so the user knows the click worked. You import it from `next/link`. It must be used in a component that is rendered _inside_ the `<Link>`, just like `useFormStatus` must be inside a `<form>`. 'use client'; import Link, { useLinkStatus } from 'next/link'; function Spinner() { const { pending } = useLinkStatus(); return pending ? <span> ⏳</span> : null; } export default function NavLink({ href, children }) { return ( <Link href={href}> {children} <Spinner /> </Link> ); } ### useServerInsertedHTML Like `useInsertionEffect`, this one is for library authors, mainly CSS-in-JS libraries. It lets you insert HTML, like a `<style>` tag, into the page while it is being rendered on the server. You import it from `next/navigation`. 'use client'; import { useServerInsertedHTML } from 'next/navigation'; export default function FontStyles() { useServerInsertedHTML(() => ( <style>{`body { font-family: Arial, sans-serif; }`}</style> )); return null; } If you build normal apps, you will almost never write this yourself. ## **Custom Hooks** A custom Hook is just a function whose name starts with `use` and that calls other Hooks inside it. That's the whole trick. You take logic that you copied into two components, move it into one function, and use it in both places. **Note:** Each component that calls a custom Hook gets its own separate state. Two components using `useToggle` don't share the same value. They only share the _code_. **useToggle** The simplest example. It's a true/false switch with a function to flip it. import { useCallback, useState } from 'react'; export function useToggle(initial = false) { const [on, setOn] = useState(initial); const toggle = useCallback(() => setOn((current) => !current), []); return [on, toggle]; } 'use client'; import { useToggle } from './use-toggle'; export default function Menu() { const [open, toggleOpen] = useToggle(); return ( <> <button onClick={toggleOpen}>Menu</button> {open && <nav>Home | Blog | Contact</nav>} </> ); } **useDebounce** It waits until the user stops typing before it gives you the new value. It's very useful before sending a search request. import { useEffect, useState } from 'react'; export function useDebounce(value, delay = 400) { const [debounced, setDebounced] = useState(value); useEffect(() => { const timerId = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(timerId); // cancel the old timer on every change }, [value, delay]); return debounced; } 'use client'; import { useState } from 'react'; import { useDebounce } from './use-debounce'; export default function SearchBox() { const [query, setQuery] = useState(''); const debouncedQuery = useDebounce(query, 500); // Use debouncedQuery in your fetch or effect. It changes only after // the user stops typing for half a second. return ( <> <input value={query} onChange={(e) => setQuery(e.target.value)} /> <p>Searching for: {debouncedQuery}</p> </> ); } `useDebounce` waits for a fixed amount of time. `useDeferredValue` doesn't use a timer. It lets React decide based on how busy the screen is. Use debounce to slow down network requests, and `useDeferredValue` to keep heavy rendering smooth. **useFetch** Here is a fetch Hook with loading and error states, and a cleanup so an old request can't overwrite a newer one. import { useEffect, useState } from 'react'; export function useFetch(url) { const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { let ignore = false; // becomes true if the url changes or the component leaves setLoading(true); setError(null); fetch(url) .then((res) => { if (!res.ok) throw new Error('Request failed'); return res.json(); }) .then((json) => { if (!ignore) setData(json); }) .catch((err) => { if (!ignore) setError(err); }) .finally(() => { if (!ignore) setLoading(false); }); return () => { ignore = true; }; }, [url]); return { data, error, loading }; } 'use client'; import { useFetch } from './use-fetch'; export default function Posts() { const { data, error, loading } = useFetch( 'https://jsonplaceholder.typicode.com/posts?_limit=5' ); if (loading) return <p>Loading...</p>; if (error) return <p>Something went wrong.</p>; return ( <ul> {data.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); } This is great for learning how Hooks fit together. In a real project, fetch in a Server Component, or use a library like TanStack Query or SWR. They handle caching, retries and much more. The rules for custom Hooks are the same as for all Hooks: * The name must start with `use`, so React and your linter can recognize it. * Call Hooks only at the top level, never inside `if`, loops or nested functions. * Call Hooks only inside components or other custom Hooks. ## **Common Mistakes to Avoid** **1. Calling a Hook after an early return or inside an`if`** React remembers your Hooks by the order they are called. If the order changes between renders, everything breaks. // ❌ Wrong function Profile({ user }) { if (!user) return null; const [name, setName] = useState(user.name); // Hook after an early return } // ✅ Right function Profile({ user }) { const [name, setName] = useState(user?.name ?? ''); if (!user) return null; } The one exception is `use`, which can be called conditionally. **2. Forgetting`'use client'` in Next.js** If you use `useState`, `useEffect` or any browser-only Hook in a Server Component, you will see an error saying the Hook only works in a Client Component. Add `'use client'` at the top of that file. Keep those files small, and only mark the interactive parts as client code, so the rest of your page stays on the server. **3. Using an effect to calculate something** If you can work it out during rendering, don't use state and an effect for it. // ❌ Wrong: extra state, extra render const [fullName, setFullName] = useState(''); useEffect(() => { setFullName(firstName + ' ' + lastName); }, [firstName, lastName]); // ✅ Right const fullName = firstName + ' ' + lastName; **4. Infinite loops in effects** // ❌ Wrong: no dependency array, so this runs after every render // and setCount triggers another render, forever useEffect(() => { setCount(count + 1); }); Another sneaky one: an object or array created during render is a new value every time, so an effect that depends on it re-runs on every render. Move it outside the component, or wrap it in `useMemo`. **5. Wrapping an input's`setState` in `startTransition`** Text inputs must update instantly. If you mark that update as low priority, the typing will lag or jump. Keep the input state urgent and use `useDeferredValue` for the slow part. **6. Calling`useFormStatus` in the same component as the form** It reads the status of a _parent_ form. Put it in a child component, like the `SubmitButton` from earlier. **7. Creating a promise while rendering and passing it to`use`** A new promise is created on every render, so React starts over each time. Create the promise in a Server Component, or use a library that caches it. **8. Using`useSearchParams` without `Suspense`** It can cause a build error or turn your whole page into client-rendered content. Wrap the component that uses it in `<Suspense>`. ## **Cheatsheet** Keep this open while you code. It has every Hook from both parts. Hook | What it does | Basic syntax ---|---|--- useState | Remembers a value | `const [state, setState] = useState(initial)` useEffect | Runs side effects | `useEffect(() => {}, [deps])` useContext | Reads shared data | `const value = useContext(MyContext)` useRef | Holds a DOM element or a value that doesn't re-render | `const ref = useRef(null)` useMemo | Remembers a calculated value | `const value = useMemo(() => compute(), [deps])` useCallback | Remembers a function | `const fn = useCallback(() => {}, [deps])` useReducer | Handles complex state logic | `const [state, dispatch] = useReducer(reducer, initial)` useLayoutEffect | Measures the DOM before paint | `useLayoutEffect(() => {}, [deps])` useImperativeHandle | Exposes methods to a parent | `useImperativeHandle(ref, () => ({ focus() {} }))` useId | Creates a stable unique ID | `const id = useId()` useTransition | Marks an update as low priority | `const [isPending, startTransition] = useTransition()` useDeferredValue | Gives a lagging copy of a value | `const deferred = useDeferredValue(value)` useSyncExternalStore | Reads data from outside React | `useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)` useInsertionEffect | Injects CSS (for library authors) | `useInsertionEffect(() => {}, [deps])` useDebugValue | Adds a label in DevTools | `useDebugValue(value)` useActionState | Tracks the result of a form action | `const [state, formAction, isPending] = useActionState(action, initial)` useFormStatus | Reads the status of the parent form | `const { pending } = useFormStatus()` useOptimistic | Shows a temporary value during an Action | `const [optimistic, setOptimistic] = useOptimistic(state, reducer)` use | Reads a promise or a context | `const data = use(promise)` useEffectEvent | Reads the latest values inside an effect (19.2+) | `const onEvent = useEffectEvent(() => {})` useRouter | Navigates from code (Next.js) | `const router = useRouter()` usePathname | Reads the current path (Next.js) | `const pathname = usePathname()` useSearchParams | Reads the query string (Next.js) | `const searchParams = useSearchParams()` useParams | Reads dynamic route params (Next.js) | `const { slug } = useParams()` useLinkStatus | Knows if a Link is loading (Next.js) | `const { pending } = useLinkStatus()` useServerInsertedHTML | Inserts HTML on the server (Next.js) | `useServerInsertedHTML(() => html)` **The rules in one place** * Call Hooks at the top level of a component or a custom Hook. Never inside `if`, loops or nested functions. * Custom Hook names start with `use`. * Hooks with state or effects need `'use client'` in Next.js. * If you can calculate it during render, skip the effect. * Always clean up timers and listeners. * Measure before you reach for `useMemo` and `useCallback`. ## **Conclusion** That's the full tour of React Hooks. Here is the short version of what to remember: * **useState, useEffect and useContext** cover most of what you will write every day. * **useTransition and useDeferredValue** keep your app smooth when updates get heavy. * **useActionState, useFormStatus and useOptimistic** make forms and instant feedback much simpler in React 19. * **use and useEffectEvent** remove a lot of old workarounds. * **Next.js Hooks** handle routing, and they only work in Client Components. * **Custom Hooks** let you write logic once and use it everywhere. You don't need to memorize all of them. Learn the core ones well, and come back to the rest when you need them. That's what the cheatsheet is for. If this guide helped you, leave a ❤️ or a 🦄, and share it with someone who is learning React. Questions, or a Hook I should explain better? Drop a comment below. Thanks for reading! 🙌
dev.to
September 24, 2026 at 9:46 PM
Learnt quite a few things that exist in React that I didn’t know about (useLayoutEffect as a recent example). I personally find coding with agents really fun, I don’t think it detracts from the experience of programming at all. I’m able to breeze past the boring stuff now.
January 3, 2026 at 12:23 AM
That’s not what I meant. How does it effects useLayoutEffect for example (which is exactly the reason it created for)? Might it cause because of this layout
thrashing? Having attached DOM node is not enough. Scheduling all measurements better be together
November 22, 2024 at 4:06 PM
```
## useEffect와 useLayoutEffect 차이점

April 28, 2023 at 4:58 AM
then i changed a useEffect to a useLayoutEffect, which allowed me to remove some ugly setTimeout code, but didn't fix the issue of the relevant component not rerendering on resize even though useLayoutEffect has the rectangle as-- oh.. is THAT it??

thanks my friendly little rubberducks! :>
December 21, 2024 at 12:26 AM
do they have a type of effect that runs before a frame? like reacts useLayoutEffect?

or just calling a function from a template?
August 10, 2026 at 6:52 PM
Mais e mais eu vejo gente criticando useEffect e chamando de useFootgun, mas sabe a real? O funcionamento do useEffect está correto, dentro do paradigma reativo, ele faz exatamente o que precisa fazer, além disso, ainda tem a opção de useLayoutEffect q dá ainda mais poder pro dev. #reactjs #bolhadev
January 23, 2025 at 11:02 PM
This video is not about perf! It's testing gaps when estimated size is way off. drawDistance is small, those flags are surprisingly heavy to render, and this is a very slow phone.

And check out the PR for details about how it works: github.com/LegendApp/le...
Fix gaps by jmeistrich · Pull Request #192 · LegendApp/legend-list
This does two things: 1. Fix gaps while scrolling When a container useLayoutEffect fires, run through all containers and if we don&#39;t already have a known size then measure the item. This is use...
github.com
April 18, 2025 at 2:50 PM
it's also worth noting that **this "layout effects don't work in SSR" warning got _removed_ in React 19!**, on the grounds that it wasn't useful at this point:

github.com/facebook/rea...
Remove layout effect warning on the server by rickhanlonii · Pull Request #26395 · facebook/react
Overview This PR unfortunately removes the warning emitted when using layout effects on the server: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server...
github.com
February 26, 2025 at 2:03 AM