[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"post-content-react-compiler-bailout-silent-skip":3},"\u003Cp>I enabled React Compiler last Tuesday and expected to delete half my \u003Ccode>useMemo\u003C\u002Fcode> calls by lunch. By Friday I was staring at React DevTools wondering why a list component with 200 items still re-rendered from scratch every time I typed a single character into a search box. The compiler was on. The build succeeded. No errors in the console. And yet the performance hadn't changed at all.\u003C\u002Fp>\n\n\u003Cp>Here's what nobody mentions in the launch posts: React Compiler doesn't throw when it can't optimize your code. It just skips that component, silently, and moves on to the next one. No warning, no error, no console message. Your component runs exactly as you wrote it — un-memoized — and you only find out when something feels slow.\u003C\u002Fp>\n\n\u003Ch2>The badge that tells the truth\u003C\u002Fh2>\n\n\u003Cp>The tell is a small badge in React DevTools. When you open the Components tab, compiled components show a \u003Cstrong>\"Memo ✨\"\u003C\u002Fstrong> badge next to their name. No badge, no memoization. I opened DevTools, looked at my \u003Ccode>SearchableList\u003C\u002Fcode> component, and the badge was missing.\u003C\u002Fp>\n\n\u003Cp>I went through the whole tree. Out of 14 components in that feature, 6 had the badge and 8 didn't. The compiler was skipping more than half my code, and I had no idea until I went looking.\u003C\u002Fp>\n\n\u003Ch2>Three patterns that triggered bailouts\u003C\u002Fh2>\n\n\u003Cp>React's docs describe a bailout as a safety feature — the compiler skips optimization when it can't statically prove your code follows the Rules of React, rather than risk changing your app's behavior. Fair enough. The bailouts I hit all fell into three buckets.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>1. Side effects during render.\u003C\u002Fstrong> I had a component that called \u003Ccode>new Date().toISOString()\u003C\u002Fcode> during render to timestamp a log row. The compiler can't cache a value that changes on every call, so it bailed on the entire component:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-javascript\">function LogRow({ message }) {\n  const timestamp = new Date().toISOString() \u002F\u002F side effect in render\n  return &lt;li&gt;{timestamp} — {message}&lt;\u002Fli&gt;\n}\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>The fix was moving the timestamp into a \u003Ccode>useEffect\u003C\u002Fcode> and reading it from state. Not glamorous, but the badge came back.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>2. An incomplete \u003Ccode>useMemo\u003C\u002Fcode> dependency array.\u003C\u002Fstrong> This one stung. I had a \u003Ccode>useMemo\u003C\u002Fcode> that filtered a list but left the filter function out of the dependency array — a stale-closure bug I'd been ignoring for months because it \"worked\":\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-javascript\">const filtered = useMemo(\n  () =&gt; items.filter(item =&gt; item.matches(activeFilter)),\n  [items] \u002F\u002F activeFilter is missing — compiler bails\n)\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>The compiler saw the missing dependency, couldn't safely extend my memoization, and skipped the whole component. Honestly? The compiler caught a bug I'd been pretending didn't exist. I added \u003Ccode>activeFilter\u003C\u002Fcode> to the array and the badge came back.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>3. An effect depending on object identity.\u003C\u002Fstrong> My search box passed a combined state object to a child, and the child had a \u003Ccode>useEffect\u003C\u002Fcode> that depended on that object's reference. Because the compiler might memoize the object differently than my manual code did, it bailed to avoid causing the effect to over-fire or loop:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-javascript\">\u002F\u002F Parent creates a new object every render\nconst searchState = { query, filters, page }\n\n\u002F\u002F Child's effect fires whenever the reference changes\nuseEffect(() =&gt; {\n  performSearch(searchState)\n}, [searchState]) \u002F\u002F identity-dependent — compiler bails\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>The fix was depending on the primitive values directly instead of the wrapper object:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-javascript\">useEffect(() =&gt; {\n  performSearch({ query, filters, page })\n}, [query, filters, page])\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Sometimes the old ways are the best ways.\u003C\u002Fp>\n\n\u003Ch2>The one directive that saved my sanity\u003C\u002Fh2>\n\n\u003Cp>When I wasn't sure whether a bug was caused by the compiler or was already living in my code, I dropped a \u003Ccode>\"use no memo\"\u003C\u002Fcode> directive at the top of the component body:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-javascript\">function SearchableList({ items }) {\n  \"use no memo\" \u002F\u002F temporarily disable compilation\n\n  \u002F\u002F ... rest of component\n}\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>If the bug disappeared with compilation off, the compiler had exposed a Rules of React violation that my manual memoization was papering over. If the bug stayed, it was mine and the compiler was innocent. I ran this on three components, found two real violations, and filed the third as \"works as intended, I guess.\"\u003C\u002Fp>\n\n\u003Cp>If you're juggling a lot of these debugging snippets like I was, keeping them one shortcut away beats digging through old commits — I started dumping bailout checklists and fix patterns into \u003Ca href=\"\u002Fsnippetark\u002F\">Snippet Ark\u003C\u002Fa> so I'm not re-deriving them every time a badge goes missing.\u003C\u002Fp>\n\n\u003Ch2>What I actually deleted\u003C\u002Fh2>\n\n\u003Cp>After two days of this, I removed 11 \u003Ccode>useMemo\u003C\u002Fcode> calls and 7 \u003Ccode>useCallback\u003C\u002Fcode> calls that the compiler now handles automatically. I kept 3 \u003Ccode>useMemo\u003C\u002Fcode> calls where the memoized value was consumed as an effect dependency and I needed explicit control over when that effect re-fired — the one case where manual memoization still earns its keep, according to React's own docs.\u003C\u002Fp>\n\n\u003Cp>The compiler can also memoize in places manual hooks can't reach, like values computed after an early conditional return. That alone cleaned up two components I'd written off as \"too awkward to optimize.\"\u003C\u002Fp>\n\n\u003Cp>If you're about to flip the compiler on, don't do what I did and assume it just works. Open DevTools, check for the badge, and \u003Ccode>\"use no memo\"\u003C\u002Fcode> anything suspicious. The compiler is smart. Your code might not be.\u003C\u002Fp>",1787363539304]