Why React Compiler Skipped Your Hottest Component: Reading Skip Reasons Instead of Guessing at Bailouts

A component that dominates your profiler has no ✨ badge in React DevTools. The instinct is to assume the compiler bailed out for a reason you can’t see, then reach for useMemo, useCallback, and memo to compensate. That instinct is usually wrong, and it costs you the one signal that would have told you what actually happened.

React Compiler is designed to skip code it cannot safely verify rather than risk changing behavior. The skip is not a failure mode you need to reverse-engineer. It is a documented state that the tooling already reports — in your editor, in DevTools, and in the compiler’s own configuration. The work is to read those reports in the right order and decide whether the skip matters for the interaction you’re trying to fix.

What the compiler actually does when it meets code it can’t verify

The React docs are explicit: “React Compiler is designed to handle code that follows the Rules of React. When it encounters code that might break these rules, it safely skips optimization rather than risk changing your app’s behavior” (React Compiler: Debugging and Troubleshooting). The same page notes that compiler errors — build-time failures — are rare precisely because the compiler prefers to skip.

That design choice has a consequence for how you read a missing badge. A skipped component is not evidence of a bug in your code. It is evidence that the compiler could not prove the component was safe to memoize. Those are different claims, and they lead to different fixes.

The compiler’s initial release focuses on two things: skipping cascading re-renders of components, and skipping expensive calculations that happen during render inside components or hooks (React Compiler introduction). If your hot component is skipped, neither of those optimizations is being applied to it. Whether that matters depends on what the component is actually doing on the interaction you care about.

Two failure directions, not one

Teams conflate two problems that the compiler treats separately.

Skipped optimization is the safe, expected outcome. The compiler saw something it could not verify and moved on. Other components in the same file, and other files in the same build, still get compiled. The docs describe this as “perfectly okay” and note the compiler “can recover and continue optimizing other components in your codebase” (React Compiler installation guide).

Incorrect optimization is the dangerous outcome. The compiler compiled a component that violates the Rules of React in a way it could not detect statically. The debugging guide names the most common shape: code written to rely on memoization for correctness, where the compiler’s memoization differs from the manual approach and produces effects that over-fire, infinite loops, or missing updates (Debugging and Troubleshooting).

These require opposite responses. A skipped hot component needs a decision about whether to widen coverage or leave it alone. A miscompiled component needs the Rules-of-React violation found and fixed. Adding manual memoization to a skipped component does nothing for the first problem and can mask the second.

The diagnostic order that replaces guessing

Before you touch the component, read what the tooling already says. The order matters because each step narrows the question.

1. Check the ESLint plugin output for that component

The compiler ships an ESLint plugin that surfaces the same analysis in your editor, and it runs independently of the compiler — you can use it even if the compiler is not enabled in your build (React Compiler installation guide). When the plugin reports a Rules-of-React violation on a component, that is the compiler telling you it skipped that component. The docs are direct about the meaning: “When it does this, it means that the compiler has skipped over optimizing that component or hook” (same page).

If the plugin is silent on your hot component, the skip is not a Rules-of-React violation the plugin can see. That is your first fork in the road.

2. Confirm the badge state in React DevTools

React DevTools v5.0+ displays a “Memo ✨” badge next to components the compiler optimized (React Compiler installation guide). The badge is the ground truth for whether the compiler touched that component in the build you are profiling. Confirm it against a production build, not a dev build — the docs are explicit that dev-mode timings are not representative and that Strict Mode double-renders in development (useMemo reference).

If the badge is present and the component still dominates the profile, the compiler did its job and the cost is somewhere else — a child that re-renders, an expensive non-component function, or work outside React. If the badge is absent, you have confirmed a skip and can move to step three.

3. Isolate with "use no memo" only if behavior is wrong

The "use no memo" directive at the top of a function prevents the compiler from optimizing it (use no memo reference). It is a bisection tool: if a runtime problem disappears when you add it, the problem is likely compiler-related. The docs frame it as “a temporary debugging tool, not a permanent solution” and warn that it takes precedence over all compilation modes and other directives.

This step is for the second failure direction — incorrect optimization — not for a skipped component. If your hot component is skipped and behaving correctly, "use no memo" changes nothing and tells you nothing. Reaching for it here is a sign you skipped steps one and two.

Why a skipped hot component is often not the lever

The compiler’s memoization is per-component and per-hook. The docs state it plainly: “React Compiler only memoizes React components and hooks, not every function” and “React Compiler’s memoization is not shared across multiple components or hooks” (React Compiler installation guide).

That has a specific consequence for the component that dominates your profile. If the expensive work lives in a plain function called during render — a sort, a filter, a transform over a large array — the compiler will not memoize that function even when it compiles the component around it. The docs recommend implementing caching for that function outside React, and profiling first to confirm it is actually expensive before complicating the code.

So the question is not “why did the compiler skip my hot component” but “what is the hot component actually spending time on.” If the answer is a non-component function, the skip is irrelevant to the cost. If the answer is cascading re-renders of children, the skip matters and the fix is to widen coverage or restructure the tree.

A decision rule you can defend in review

Work through these in order. Each one produces a recorded answer, not a guess.

  1. Does the ESLint plugin report a violation on the component? If yes, fix the Rules-of-React violation. That widens compiler coverage and is the correct fix regardless of performance. If no, continue.
  2. Is the ✨ badge absent in a production build? If yes, you have a confirmed skip with no plugin-reported cause. Continue. If the badge is present, the compiler optimized the component and the cost is elsewhere.
  3. What does the profiler attribute the time to? If the cost is a non-component function called during render, the compiler’s per-component memoization will not help it. Cache that function outside React, guided by a measured before/after. If the cost is child re-renders, the skip is the lever — widen coverage or restructure so children receive stable props.
  4. Only then consider manual memoization. The React docs are clear that useMemo, useCallback, and memo are performance optimizations, not correctness tools, and that a single value that is “always new” is enough to break memoization for an entire component (memo reference). Add them where the profiler shows a measured cost, not where a badge is missing.

Step four is where most teams start. That is the mistake. Manual memoization on a skipped component does not restore the compiler’s optimization — it adds a second, hand-maintained caching layer whose correctness you now own. The docs note that with the compiler enabled you typically don’t need React.memo anymore because the compiler applies the equivalent automatically (memo reference). Adding it back to a skipped component is not the same optimization; it is a different one with different failure modes.

Configuration choices that change what “skipped” means

Two compiler options affect whether a component is compiled at all, and both are worth checking before you conclude anything about a skip.

compilationMode controls which functions the compiler selects. The default is intelligent detection; 'annotation' compiles only functions marked with "use memo" (Configuration reference). If your project is in annotation mode, a component without the directive is skipped by configuration, not by analysis. That is a different problem with a different fix.

panicThreshold determines whether the compiler fails the build or skips problematic components (same page). The docs show panicThreshold: 'none' as the recommended production setting, which means problematic components are skipped rather than failing the build. If your team set this, a skip is the configured behavior, not a surprise.

Check both before you profile. A skip caused by configuration is not a signal about your code.

What to record so the next person doesn’t guess

The discipline that separates a performance-engineering decision from a reflex is a recorded delta. Before you change anything, capture:

  • The React and compiler versions, pinned exactly. The compiler’s behavior and the DevTools badge depend on both.
  • The ESLint plugin output for the component, verbatim.
  • The ✨ badge state in a production build.
  • The profiler’s attribution for the interaction: which components rendered, how many times, and where the time went.
  • The interaction’s INP or long-task duration from the Chrome DevTools Performance panel, measured on a throttled CPU.

After the change, capture the same five. If the delta is not measurable, the change was not a performance fix — it was a guess that happened to compile.

The React docs make the same point about useMemo: measure with console.time first, and if the logged time is not significant, memoization is not warranted (useMemo reference). The same standard applies to every decision that follows a skip.

FAQ

My component has no ✨ badge but the ESLint plugin reports nothing. What does that mean?

It means the compiler skipped the component for a reason the plugin did not surface as a Rules-of-React violation. Check your compilationMode and panicThreshold settings first — a skip caused by annotation mode or a permissive panic threshold is configuration, not analysis. If both are at defaults, the skip is the compiler’s safe behavior on code it could not verify, and the next question is whether the component’s cost is actually attributable to the missing optimization.

Should I add React.memo to a skipped hot component?

Only if the profiler shows the component re-rendering with props that are shallowly equal, and only after you have confirmed the skip is not a Rules-of-React violation you could fix instead. The docs note that memo is “completely useless if the props passed to your component are always different” (memo reference), which is common when a parent passes inline objects or functions. Fix the prop stability first; the memo wrapper is the last step, not the first.

Does "use no memo" tell me why the compiler skipped a component?

No. It tells the compiler to skip a function it might otherwise have compiled. It is a bisection tool for isolating whether a runtime problem is compiler-related, not a diagnostic for why a skip occurred. The docs describe it as a temporary debugging tool and warn against leaving it in place (use no memo reference).

Is React Compiler stable enough to rely on for this analysis?

The retrieved docs describe the compiler as experimental with rough edges in one page and reference React 19 compatibility in another. The debugging guide and configuration reference are the authoritative sources for current behavior, and both should be read at the version you have pinned. Do not generalize from one page’s framing to a claim about production readiness across all apps.

What if the expensive work is in a plain function, not a component?

The compiler will not memoize it. The docs state that the compiler “only memoizes React components and hooks, not every function” and recommend implementing caching for genuinely expensive functions outside React, after profiling confirms the cost (React Compiler installation guide). A skipped component that calls such a function is not the problem; the function is.

The short version

A missing ✨ badge is a question, not an alarm. Read the ESLint plugin output, confirm the badge in a production build, and check your compilationMode and panicThreshold before you conclude anything about the compiler’s analysis. If the skip is real and the profiler attributes the cost to child re-renders, widen coverage or restructure the tree. If the cost is a non-component function, cache it outside React. Reach for manual memoization last, with a measured delta in hand, and record the versions and numbers so the next person reads evidence instead of guessing at a bailout.

The Period After ‘meh’ Is the Whole Design

On a dark shirt, centered, in lowercase, sits the word meh. — and the period is doing more work than the word. The native garment preview shows the lettering at a very small scale, and no animal illustration appears anywhere in the design. That is the entire graphic. Before you decide it is too little, look at what the period is actually saying.

The common assumption is that a small print is a timid print, a design that ran out of confidence. The opposite is true here. A large MEH across the chest is a performance. It announces indifference loudly, which is a contradiction the wearer has to carry around all day. A small meh. does not announce anything. It is a note left on the table, not a speech from the podium.

The period changes the grammar of the word

Strip the period and you have an interjection. Meh on its own is a sound, a shrug rendered in letters, and it invites a response. Someone sees it and says something. The word is a conversational opening, and the wearer becomes responsible for the conversation.

Add the period and the word becomes a sentence. A one-word sentence, but a complete one, with the finality that a full stop carries. The period is the difference between a person saying meh to you and a person writing meh. in a log and closing the file. The first is a mood. The second is a verdict.

That is my reading of the design, not a claim about the designer’s intent. What I can observe is the punctuation and the scale. What I infer is that the combination produces a specific kind of understatement — the kind that does not ask to be agreed with.

Why the small scale is the argument, not a compromise

There is a practical reason small type reads as more sincere than large type, and it has nothing to do with shyness. Large type is addressed to the room. Small type is addressed to whoever leans in. A shirt that requires proximity to be read is making a claim about who the message is for: not the crowd, not the meeting, not the group chat — the one person close enough to notice.

For readers who spend their working lives in code review and incident channels, that distinction is familiar. The most useful comment in a pull request is rarely the longest one. The message that changes a decision is often a single line, quietly placed, that does not repeat itself. A design that trusts four letters and a period to carry the whole idea is operating on the same principle. It is not minimal because it lacks ideas. It is minimal because it has exactly one and refuses to dilute it.

This is also why the absence of an animal illustration matters. The listing title mentions introversion and sarcasm, and the temptation with those themes is to add a mascot — a cat, an owl, something with a flat expression to do the emotional labor. The preview shows none of that. The shirt is typography and negative space. Whatever introversion the design evokes comes from the restraint, not from a cartoon standing in for it.

A slogan versus a usable promise

Most slogan shirts fail because they make a promise the wearer cannot keep. A shirt that says something aggressive commits the wearer to being that person at the grocery store, at the DMV, at a family dinner. The garment becomes a small performance obligation. meh. makes almost no obligation at all. It is a boundary statement with the volume turned down: present, legible, and finished.

That is the difference I keep coming back to between a slogan and a usable promise. A slogan is written for the moment of purchase. A usable promise is written for the third hour of wearing it. The period is what makes this one usable. It closes the sentence so the wearer does not have to.

What the design does not claim

It is worth being precise about the limits of a reading like this. The shirt does not demonstrate anything about introversion as a psychological category, and it does not measure anything about the wearer’s mood, productivity, or social battery. It is a piece of printed language on fabric. The observation that the lettering is small, lowercase, centered, and punctuated is a description of the artwork. The argument that the period converts a shrug into a verdict is an interpretation, and I am labeling it as one.

What the design does do, plainly, is commit to a single typographic decision and execute it without decoration. In a category crowded with loud graphics, that is a coherent position. The shirt is available as a standard t-shirt through the listing for the Meh Funny Sarcastic Introvert Minimalist Saying shirt.

Editorial note: This publication and Quirky Cotton Collective share an owner.

The technique: read the punctuation before the word

The concrete thing to take from this design, whether or not you ever wear it, is a reading habit. When a message is short, the punctuation is not decoration — it is the instruction for how to hear the message. A period closes. A question mark opens. An ellipsis stalls. The next time a terse comment lands in a code review or a status update and you are tempted to read it as hostile, check the punctuation first. meh and meh. are different messages from different people, and only one of them is finished with you.

The Crossed-Out Hand Is the Joke: Reading ‘Not Friendly Do Not Touch’

The shirt reads NOT FRIENDLY DO NOT TOUCH in warning-sign lettering. Above the words sits a yellow caution triangle. Inside the triangle, a hand is crossed out. That is the whole design. No slogan beneath it, no winking subtext, no second line to soften the first. The visual grammar is borrowed wholesale from industrial signage — the same triangle-and-prohibition logic that tells you not to enter a restricted area or not to put your hand near a moving blade — and applied to a person.

I keep coming back to the crossed-out hand. It is doing more work than the words. The words state a condition: not friendly. The triangle states a category: hazard. The crossed-out hand states an instruction: do not touch. Three layers, each one narrower than the last, and the narrowest one is the only one that is actually actionable. You cannot do anything about someone being not friendly. You can very much do something about your hand.

Why the prohibition symbol reads faster than the sentence

Warning-sign conventions are among the most over-learned visual patterns in daily life. A yellow triangle with a black border means caution before you have read a single word. A diagonal slash through a pictogram means prohibited before you have identified the object underneath it. The design on this shirt leans on that over-learning. The eye hits the triangle, registers caution, hits the slash, registers no, and only then reads the lettering — which confirms what the geometry already said.

That ordering matters. If the shirt were only the words, it would be a statement. With the triangle and the slash, it becomes a sign. Signs are instructions. Statements invite debate; instructions do not. The crossed-out hand converts a personality description into a boundary with a diagram attached. That is a different social object entirely.

The difference between a slogan and a usable promise

Most novelty shirts are slogans. They assert a mood, a preference, a tribe. They are meant to be read and enjoyed, and they ask nothing of the reader. This one asks something. The crossed-out hand is a request, phrased in the imperative, backed by the visual authority of a hazard placard. Whether anyone honors it is out of the wearer’s control, but the shirt has at least made the request legible in a format people already know how to parse.

I think that is the actual joke, and it is a drier joke than the words alone suggest. The humor is not “I am unfriendly.” The humor is the mismatch between the bureaucratic seriousness of the sign and the intimate scale of the thing being signed — one person’s personal space, rendered as if it were a loading dock. The caution triangle is absurd here because the hazard is a human mood, not a physical danger. The absurdity is the point. The design borrows the full weight of industrial safety language and spends it on “leave me alone,” and the collision between those two registers is what makes it funny rather than merely rude.

What the design does not claim

It is worth being precise about what this artwork is and is not. It is a graphic in the visual vocabulary of caution signage. It is not a safety certification, not a real hazard placard, and not evidence of anything about the wearer beyond the fact that they chose it. I am reading the design, not diagnosing the person. The shirt does not cure introversion, improve boundaries, or make anyone a better colleague. It is a printed joke that happens to be structured like an instruction, and the structure is what I find interesting.

The same caution applies to the word “introvert” in the listing title. That is retail framing, not something the artwork itself spells out. The printed design is the warning-sign lettering, the triangle, and the crossed-out hand. Everything else is context supplied by the listing, and I am treating it as such.

Why this reads as a work-culture object

For readers who spend their days in shared codebases and shared calendars, the sign language here is oddly familiar. Engineering culture already runs on terse, standardized signals: the red build, the do-not-merge label, the incident severity tag, the on-call handoff. We are fluent in prohibition symbols. A shirt that renders a personal boundary in that same register is legible to exactly the kind of person who spends their afternoon reading status badges.

That is not a claim that the shirt is about software, and I am not going to pretend it demonstrates anything about team dynamics. It is a shirt. But the reason it lands for this audience is that the format is already part of their visual diet. The joke works because the reader has internalized the grammar of the caution triangle somewhere between a fire-safety poster and a failing pipeline. The shirt just points that grammar at a person and lets the mismatch do the rest.

If you want the object itself, it is the Not Friendly Do Not Touch warning-sign shirt from Quirky Cotton Collective. The design is the lettering, the yellow triangle, and the crossed-out hand — nothing more elaborate than that, and nothing less.

The one technique: read the sign before the sentence

The concrete takeaway is a reading habit, not a purchase. Next time a design borrows a familiar sign system, look at the geometry first and the words second. Ask what the symbol is instructing before you ask what the text is saying. On this shirt, the crossed-out hand is the instruction and the lettering is the confirmation. Once you see that ordering, the joke stops being a slogan and becomes a small, well-built piece of visual rhetoric — a boundary expressed in the one format almost everyone already knows how to obey.

Editorial note: This publication and Quirky Cotton Collective share an owner.

The Roots Are the Joke: Reading ‘Let’s Root For Each Other’

The first thing I noticed on the shirt was not the slogan. It was the soil line. Four root vegetables — carrot, radish, beet, onion — sit in a row, each with a face, each with its taproot and fibrous roots drawn down into the dirt. The words LET’S ROOT FOR EACH OTHER AND WATCH EACH OTHER GROW wrap around them. The pun is obvious enough: root as in cheer, root as in the part underground. But the drawing does something the sentence alone does not. It puts the roots on display. The joke is not just that the word has two meanings. The joke is that the visible, cheerful part of the plant is only half the picture, and the half doing the work is the half you cannot see from above.

That is a useful thing to look at closely, because the slogan is the kind of line that gets repeated in team channels and on offsite slides without anyone asking what it actually promises. Let’s root for each other is a statement about support. And watch each other grow is a statement about growth. Put them together and you have a sentence that sounds warm and cooperative, but it also quietly assumes that growth is the thing worth watching, and that everyone’s growth is visible, and that the watching is mutual. The artwork is smarter than the sentence, because the artwork shows you the roots. It shows you that the growth above the soil depends on something below the soil that nobody is applauding.

What the drawing actually shows

Look at the four vegetables in order. The carrot is long and tapered, its orange body mostly underground, with a tuft of green above. The radish is round and red, sitting close to the surface. The beet is bulbous and dark, with a thick root running down. The onion is layered and pale, its roots spreading out in a small fan. Each one has a face — eyes, a smile — and each one is drawn so that the edible part and the root system are both visible. The soil line is not a hard border. It is a cross-section, a cutaway, the kind of diagram you would draw if you wanted to explain what is happening beneath a garden bed.

That cross-section is the whole argument of the design. A normal gardening illustration shows you the pretty part: the flower, the leaf, the ripe tomato. This one shows you the part that does the work. The carrot’s taproot is not decorative. It is how the carrot gets water and nutrients. The radish’s fibrous roots are not a flourish. They are the reason the radish exists. The drawing is a small diagram of dependence, and the pun only lands because the diagram is accurate enough to be legible.

I am not making a botanical claim beyond what the drawing shows. The roots are drawn in a stylized, cartoonish way, and I am reading the visual language, not a horticulture textbook. But the visual language is clear: the smiling part and the working part are the same plant, and the working part is underground.

The two meanings of rooting, and why the second one matters

In American English, to root for someone means to cheer for them, to want them to succeed. That is the meaning the slogan leans on first. It is a friendly, low-stakes word. You root for a sports team. You root for a colleague’s promotion. You root for the underdog. It costs you nothing and it feels good.

The second meaning is the one the drawing supplies. To root is also to establish a root system, to put down the part that holds you in place and feeds you. A plant that has not rooted cannot grow. A plant that has rooted badly will grow for a while and then fall over. The shirt puts both meanings in the same sentence and lets the picture do the work of distinguishing them. The cheering is above ground. The rooting is below ground. The sentence says let’s root for each other, and the drawing says and here is what rooting actually looks like.

That distinction is worth carrying into how teams talk about support. Cheering is easy. It is a message in a channel, a thumbs-up on a pull request, a kind word in a retro. Rooting, in the second sense, is harder. It is the review that catches the bug before it ships. It is the documentation nobody reads until they need it. It is the on-call rotation that gives someone a night off. It is the unglamorous, underground work that makes the visible growth possible. The shirt does not say any of that. But the drawing does, if you look at where the roots are.

The growth clause is the part to be careful with

The second half of the slogan — and watch each other grow — is where the design gets interesting, and where I would push back a little. Growth is not always good. A tumor grows. A backlog grows. A codebase can grow in ways that make it harder to change. The word grow carries a positive charge in most team contexts, but the charge is not inherent to the word. It is a assumption, and the shirt repeats it without examining it.

That is not a flaw in the shirt. It is a slogan, and slogans are supposed to be simple. But if you are the kind of person who reads a shirt closely, the growth clause is the place to notice the gap between the sentiment and the reality. Watch each other grow assumes that growth is visible, that it is mutual, and that the watching is a form of support rather than surveillance. In a team, those assumptions do not always hold. Some growth is invisible for a long time. Some growth happens in a direction nobody else is watching. And some watching is not support at all — it is performance review.

The artwork rescues the slogan here, too. The roots are not watching each other. They are sharing the same soil. The carrot is not monitoring the beet’s progress. The onion is not filing a report on the radish. They are simply in the same ground, drawing from the same resources, and the drawing shows them side by side without ranking them. That is a better model of mutual growth than the sentence alone provides. The sentence says watch. The drawing says share the ground. The second is more useful.

What the shirt is, and what it is not

This is a standard t-shirt from Quirky Cotton Collective, sold on Amazon. The design is a gardening and mutual-growth pun, and the visual observation is the one I have been reading: the slogan surrounds four smiling root vegetables with their roots visible in soil. The shirt is a piece of clothing with a joke on it. It is not a manifesto, a management framework, or a claim about how teams should operate. I am reading the design because it is well drawn and because the pun rewards a second look, not because a shirt can solve a culture problem.

If you want the object itself, it is here: Let’s Root For Each Other And Watch Each Other Grow Garden. The listing is the source for the design description, and the visual observation above is the basis for everything I have said about the artwork. I have not worn it, tested it, or seen it on a body. I am reading the drawing.

Editorial note: This publication and Quirky Cotton Collective share an owner. This is not an independent review.

The one technique: read the diagram, not just the slogan

If there is a technique in this piece, it is this: when a slogan arrives with a picture, read the picture first. The sentence is the claim. The drawing is the evidence. In this case, the sentence says root for each other and watch each other grow, and the drawing shows four plants whose visible faces depend on roots nobody is cheering for. That is a more honest account of mutual support than the words alone. The next time a team slogan crosses your desk, look for the soil line. If there isn’t one, ask what is underground. The answer is usually where the actual work is.

Pinning a 340ms Long Task to the Exact React Component Renders Inside It

A 340ms long task in the Chrome DevTools Performance panel is a symptom, not a diagnosis. The Main track tells you the main thread was busy for 340ms; it does not tell you which React component renders are inside that window, whether they ran on the blocking lane or a transition lane, or whether the cost was real render work or wasted work. This article is a procedure for closing that gap: read the Scheduler track first to classify the work, then the Components track to name the renders, then cross-check with <Profiler> to decide whether the fix is “make the render cheaper” or “move it off the blocking path” — and re-record to prove the number moved.

Two version pins matter before anything else. React Performance tracks are only available in development and profiling builds of React, and the profiling instrumentation that powers them adds overhead, so it is disabled in production builds by default (React docs: React Performance tracks). The 340ms figure in this article is an illustrative example, not a measurement of any real application. Treat every millisecond you quote as something you recorded on your own build.

Why the Main track alone can’t answer the question

The Performance panel represents main thread activity as a flame chart: the x-axis is time, the y-axis is the call stack, and clicking an event shows duration, self duration, a link to the source line, and a stack trace in the Summary tab (Chrome DevTools Performance features reference). That is enough to see that a task ran long. It is not enough to see React’s own view of the work, because React’s render phase, commit phase, and passive effects are not separate browser tasks — they are slices inside the same long task, often inside minified framework code.

The wrong approach at scale is to open the flame chart, find the widest yellow bar, and start memoizing whatever component name appears near it. That fails for two reasons. First, the widest bar is frequently React internals, not your component. Second, even when you land on the right component, you have not learned why it rendered or whether it rendered on the blocking lane — and those two facts determine which fix is correct.

Step 1: Get onto a build that emits React tracks

React Performance tracks appear automatically in traces recorded with the Performance panel of browsers that provide extensibility APIs, but only in development and profiling builds. In a profiling build, only the Scheduler track is enabled by default, and the Components track lists only components inside subtrees wrapped with <Profiler> — unless the React Developer Tools extension is enabled, in which case all components are included in the Components track even if they are not wrapped (React docs).

To use the profiling build, React documents swapping react-dom/client for react-dom/profiling, and recommends aliasing at build time via bundler aliases rather than editing every import (React docs). A webpack-style alias looks like this:

// next.config.js or webpack config — illustrative
config.resolve.alias['react-dom/client'] = 'react-dom/profiling';

Two cautions. The React docs note that a framework may have built-in support for enabling the profiling build; check your framework’s documentation rather than assuming the alias is the supported path. And do not quote absolute timings from the default development build — it is instrumented for warnings and its absolute numbers are not evidence. The profiling build is the appropriate source for numbers you intend to defend.

Step 2: Read the Scheduler track before the Components track

The Scheduler track has four subtracks, one per priority lane: Blocking (synchronous updates, which could have been initiated by user interactions), Transition (non-blocking background work, usually initiated via startTransition), Suspense (work related to Suspense boundaries, such as displaying fallbacks or revealing content), and Idle (the lowest priority work, done when nothing higher is pending) (React docs).

Every render pass also shows its phases on the timeline: Update (what caused the new render pass), Render (React calls the render functions of the updated subtree), Commit (React submits changes to the DOM and runs layout effects such as useLayoutEffect), and Remaining Effects (passive effects, usually after paint — with the documented exception of discrete events like clicks, where this phase can run before paint) (React docs).

This ordering is the whole method. If your 340ms task shows the expensive render on the Blocking subtrack, the fix is either to make that render cheaper or to move it to a lower priority lane. If it already sits on Transition, moving it will not help — you have a genuine render-cost problem and need to reduce the work. Reading the Components track first skips this fork and sends you toward the wrong fix roughly half the time.

Step 3: Name the renders on the Components track

The Components track visualizes component render durations as a flamegraph, where each entry represents the duration of the corresponding component render and all its descendant children — so a wide bar near the root is inclusive time, not that component’s own cost (React docs). The track follows the same color scheme as the Scheduler phases, which is what lets you line a render entry up against the Blocking or Transition bar above it.

Effects appear on this track too, but React only displays effects that ran for 0.05ms or longer or that triggered an update, to keep the track readable (React docs). If you are hunting a long task and the Components track looks empty for a subtree you know rendered, check whether the work was an effect below that threshold rather than a render.

In development builds, clicking a component render entry lets you inspect potential changes in props (React docs). That is the fastest way to spot an unstable inline prop or a context value that changed identity on every parent render.

Step 4: Catch updates scheduled during render

One pattern deserves its own pass through the trace. If an update was scheduled during a render pass, React can discard completed work and start a new pass — a cascading update. In development builds, React can show which component scheduled the new update, and clicking the “Cascading update” entry displays an enhanced stack trace including the name of the method that scheduled it (React docs). A useEffect that sets state on every render is the classic source; the Cascading update entry names it faster than reading the flame chart will.

Step 5: Use <Profiler> to choose between “expensive” and “wasted”

The Components track tells you which renders are inside the long task. It does not, by itself, tell you whether those renders were necessary. That is what <Profiler> is for. It measures rendering performance of a React tree programmatically through an onRender callback that receives id, phase, actualDuration, baseDuration, startTime, and commitTime (React docs: <Profiler>).

The two durations are the fork in the road:

  • actualDuration is the milliseconds spent rendering the profiled subtree and its descendants for the current update, and it indicates how well the subtree makes use of memoization (React docs).
  • baseDuration estimates how long it would take to re-render the entire subtree without any optimizations, calculated by summing the most recent render durations of each component in the tree (React docs).

If actualDuration sits close to baseDuration on the update that produced your long task, memoization is not skipping much — this is a genuine render-cost problem, and the fix is to reduce the work or move it off the blocking lane. If actualDuration is far below baseDuration, memoization is already doing real work and adding more of it is unlikely to move the number. commitTime is shared between all profilers in a commit, which lets you group measurements from several <Profiler> boundaries that landed in the same commit (React docs).

Two constraints on this tool. Profiling adds overhead and is disabled in the production build by default; opting into production profiling requires a special production build with profiling enabled (React docs). And components wrapped in <Profiler> are marked in the Components track even in profiling builds, which is how you get named entries without the DevTools extension (React docs). Instrument the subtrees you suspect, not every component — each <Profiler> adds CPU and memory overhead.

A worked example, clearly labeled as hypothetical

Suppose a filter input sits above a large table, and typing produces a 340ms long task. The procedure runs like this:

  1. Record one keystroke on the profiling build with CPU throttling on. Find the 340ms task on the Main track.
  2. Scheduler track: the table render appears on the Blocking subtrack, in the same frame as the input. That tells you the work is synchronous and on the interaction path.
  3. Components track: the flamegraph names the table subtree, and the inclusive bar is wide while the leaf rows are individually cheap. The cost is spread across many renders, not concentrated in one slow component.
  4. <Profiler> around the table: actualDuration is close to baseDuration on the keystroke commit. Memoization is not the lever here.
  5. One change: split the update so the input value commits synchronously and the table update runs at a lower priority — for example by deferring the value the table reads. The mechanism is documented in useTransition, which describes marking state updates as Transitions so they are non-blocking and interruptible.
  6. Re-record. The keystroke should now produce a short Blocking bar and the table render should appear on the Transition subtrack. The render cost did not shrink; it stopped blocking. That distinction is the payoff of reading the Scheduler track first.

No millisecond improvement is asserted here, because none was measured. The claim the procedure supports is structural: the work moved lanes, and the trace shows it. Whether that improves a field metric is a separate measurement you have to take.

What this method does not cover

React’s Performance tracks are an experimental adoption of Chrome’s Performance panel extensibility API, which surfaces client and server component rendering as well as React scheduling data (Chrome for Developers). The extensibility API itself offers two mechanisms: console.timeStamp, designed for minimal runtime overhead and not adding entries to the browser’s internal performance timeline, and the User Timings API (performance.mark / performance.measure), which does add entries and supports richer detail (Chrome DevTools extensibility API). Custom tracks only appear when Show custom tracks is enabled in Capture settings — check that before concluding the tracks are missing.

Server Components and Server Requests tracks are development-only, and Server tracks are not available in profiling builds (React docs). If your long task is on the client but your data comes from a server component, the server-side timing will not appear in the same trace you recorded on the profiling build.

The loop, not the fix

The durable skill is the loop: baseline on the profiling build, read Scheduler before Components, use actualDuration versus baseDuration to pick the category of fix, change exactly one thing, re-record, and keep the change only if the trace shows the work moved or shrank. A 340ms long task is not a bug report until you can point at the renders inside it and say which lane they ran on and why they rendered. Everything before that point is guessing with a flame chart.

FAQ

Why don’t I see a Components track in my trace?

Three likely causes. You are on a production build, where the instrumentation is disabled by default. You are on a profiling build without the React DevTools extension, in which case only <Profiler>-wrapped subtrees are listed. Or Show custom tracks is off in Capture settings.

Can I use these tracks in production?

Not with the default production build. The profiling instrumentation adds overhead and is disabled there by default; React documents a special production build with profiling enabled as the opt-in path. Chrome’s console.timeStamp API is designed for low-overhead instrumentation suitable for hot paths, but that is a separate mechanism you would wire up yourself.

Should I trust the millisecond numbers from the development build?

No. The development build is instrumented for warnings and its absolute timings are not evidence for optimization decisions. Use the profiling build for numbers you intend to quote, and prefer the delta between two recordings over any single absolute value.

Does a wide bar on the Components track mean that component is slow?

Not necessarily. Each entry represents the duration of that component render and all its descendant children, so a wide bar near the root is inclusive time. Look at the leaves and at the actualDuration versus baseDuration comparison before concluding where the cost lives.

The Tile Symbols Are the Joke: Reading ‘I Wear This Shirt Periodically’

The shirt reads I WEAR THIS SHIRT PERIODICALLY, and beneath the sentence sit four periodic-table-style tiles: 53 I, 74 W, 22 Ti, 16 S. Read the symbols in order and you get I W Ti S. That is not the word periodically. It is not even a clean abbreviation of it. The tiles are the joke, and the sentence above them is the setup that makes the joke land.

Most slogan shirts ask you to agree with a sentence. This one asks you to read a diagram. The sentence is grammatical and complete on its own — I wear this shirt periodically — but the tiles underneath are not a translation of that sentence into chemistry. They are a near-miss. The first tile, I, is iodine, symbol I, atomic number 53. The second, W, is tungsten, symbol W, atomic number 74. The third, Ti, is titanium, 22. The fourth, S, is sulfur, 16. Put the symbols together and you get I W Ti S, which is close enough to periodically to trigger the pun and wrong enough to be funny.

That gap is the whole design. A literal periodic-table spelling of periodically would need tiles for P, Er, I, O, Dy, C, Al, Li — or some other arrangement — and it would be long, fussy, and probably unreadable at shirt distance. The designer did not attempt that. Instead, four tiles stand in for the word, and the reader is trusted to hear the sentence and see the symbols at the same time. The joke is not that the shirt is periodically spelled in elements. The joke is that the tiles almost spell it, and the sentence above tells you what you are supposed to hear.

This is a different kind of shirt humor from the boundary-wording designs I have written about before. Those shirts work by stating a rule — Not Friendly Do Not Touch, meh. with a period — and letting the punctuation or the crossed-out hand carry the attitude. This one works by misdirection. The sentence is plain. The tiles are the puzzle. You read the sentence, you glance at the tiles, you try to sound them out, and the mismatch is the payoff. It rewards a second look, which is more than most slogan shirts can claim.

The element choices are not arbitrary, either. Iodine, tungsten, titanium, sulfur — those are real elements with real symbols, and the atomic numbers printed on the tiles are correct for each. That matters because the joke only works if the tiles look like a real periodic table excerpt. If the numbers were wrong or the symbols were invented, the design would collapse into nonsense. The correctness of the tiles is what makes the near-miss feel deliberate rather than sloppy. The designer knows the table well enough to fake a spelling with it.

There is also a quiet joke about frequency in the word periodically. To wear something periodically is to wear it now and then, on a schedule you do not specify. The periodic table is, of course, a table of periods — rows — and the tiles are arranged in a row. So the shirt is doing two things at once: it is punning on periodic as in periodic table, and it is making a low-stakes claim about how often the shirt gets worn. That second reading is the one that makes it a wearable object rather than a chemistry quiz. The shirt is not saying I am a chemist. It is saying I wear this sometimes, and I am enough of a nerd to make a table joke about it.

For a technical reader, that is a familiar register. It is the same impulse behind a diagram joke on a sticker or a terse comment in a code review: the pleasure is in the compression. A sentence that could have been plain is instead encoded, and the encoding is the point. The shirt does not need you to laugh out loud. It needs you to notice the tiles, read the symbols, and appreciate that someone chose Ti and S instead of spelling the word out. That is a small, specific craft, and it is the reason the design holds up better than a generic science pun shirt.

The object itself is a standard t-shirt from Quirky Cotton Collective, listed as I Wear This Shirt Periodically Funny Chemistry Science Pun. The design is the thing worth talking about: four tiles, four symbols, one sentence, and a gap between them that does the work. If you are going to wear a joke, this is the kind that rewards the people who actually look at it.

Editorial note: This publication and Quirky Cotton Collective share an owner.

The Dashed Circle Is the Joke: Reading the ‘I Had Potential’ Ramp-and-Ball Diagram

Two circles, one curve, four words. The shirt reads I HAD POTENTIAL in white capitals, and beneath the text a white curved ramp descends from an upper-left ledge to a lower-right flat. At the top of the ramp sits a dashed, empty circle. At the bottom rests a solid white ball. That is the entire design. No arrows, no axes, no equations, no punchline in smaller type. The joke is the arrangement, and the arrangement is doing something more precise than the phrase alone.

Most slogan shirts treat words as the product and the graphic as decoration. This one inverts that. The sentence is the setup; the diagram is the delivery. Read the words without the drawing and you have a familiar complaint — a person announcing that their promise went unfulfilled. Read the drawing without the words and you have a physics sketch: a body at rest on a high ledge, a curved path down, a body at rest on a low ledge. The humor lives in the gap between those two readings, and the gap only closes because the dashed circle is dashed.

What the dashed circle actually says

In a physics diagram, a dashed outline conventionally marks a position that is not occupied — a predicted location, a reference point, a body that could have been there. The solid ball is where the object ended up. The dashed circle is where it started, or where it was supposed to go, depending on how you read the ramp. The design does not label either one. That omission is the point.

I read the dashed circle as the potential itself: a shape with the right dimensions and no mass. It sits at the top of the curve, at the high point, where gravitational potential energy would be greatest if anything were actually there. The solid ball sits at the bottom, where that energy has been spent. The shirt is not saying the ball fell. It is saying the ball is still at the bottom, and the outline at the top is all that remains of what could have rolled.

That is a more specific joke than “I had potential” as a standalone phrase. The phrase is passive — something was had, and now it is gone. The diagram is structural: it shows the high position, the low position, and the empty outline that connects them. The dashed circle is not a missing object in the sense of something lost. It is a missing object in the sense of something that was never solid to begin with. The potential was always an outline.

Why the curve matters more than the words

A straight diagonal would have made this a different shirt. A straight line from upper left to lower right is the universal shorthand for decline — the graph going down, the trend line, the stock chart. It is blunt and a little lazy. The curved ramp is not a trend line. It is a physical path, and a curved path implies acceleration, friction, and a surface that shapes the motion rather than merely recording it.

The curve also makes the diagram look like a problem from a textbook rather than a meme template. That is the visual register the design is borrowing: the clean white line on a dark ground, the unlabeled axes, the two-body setup. It reads as a figure from a chapter on energy conservation, except the figure has been captioned with a sentence about a person. The mismatch between the clinical drawing and the self-pitying text is where the sarcasm lands. The shirt is not sad about the ball. It is deadpan about it.

I would not claim the design is a correct physics diagram. The dashed circle and the solid ball are not labeled, the ramp has no stated height, and nothing about the drawing tells you whether the ball rolled or was placed. What the drawing does is supply the grammar of a physics problem — potential, path, rest — and let the caption supply the human disappointment. The joke works because the grammar is recognizable, not because the physics is rigorous.

The difference between a slogan and a usable promise

This is where the shirt becomes more than a novelty. The phrase “I had potential” is a slogan: it names a state and invites agreement. The diagram is closer to a promise: it shows a starting condition, a path, and an end condition, and it lets you check whether the end condition matches the starting one. The dashed circle at the top and the solid ball at the bottom do not match. That is the whole argument of the design, and it is an argument about follow-through.

Technical readers spend a lot of time around diagrams that make claims. A architecture sketch, a flow chart, a state machine, a latency budget — each one is a promise about how something moves from one state to another. The useful ones are honest about which states are occupied and which are hypothetical. The dashed circle is the honest part of this shirt. It marks the position that was never filled, and it does so without a caption explaining that it was never filled. The drawing trusts you to notice.

That is the craft in the design, and it is also the reason the shirt works as an object rather than a gag. A slogan shirt asks you to agree with a sentence. A diagram shirt asks you to read a relationship. The relationship here is between a high empty outline and a low solid ball, and the sentence above it tells you which one is the person. The sarcasm is not in the words. It is in the fact that the words are placed above a diagram that quietly contradicts them.

Reading the object without overreading it

The shirt is sold as I Had Potential Funny Physics Pun Sarcastic Minimalist Quote by Quirky Cotton Collective, and the retail title leans on “physics pun” and “minimalist” in ways the artwork does not fully earn. There is no pun in the strict sense — no word doing double duty through sound. The double meaning is visual: potential as a human attribute and potential as a stored quantity. The minimalism is real, though. Four words, two circles, one curve, and a lot of empty ground.

I would not read the shirt as a statement about any particular profession, and I would not claim it demonstrates anything about engineering, security, or software. It is a diagram joke about a person, and the person is the one wearing it. The design does not specify what the potential was for. That blankness is what lets it work as a work-culture object: the dashed circle can be a stalled project, an abandoned refactor, a skill that never got the reps, or a version of yourself that stayed an outline. The shirt does not pick. It just draws the shape.

The one thing the design does insist on is that the ball is at rest. There is no motion line, no arrow, no dust. The solid ball sits on the flat lower ledge as if it has been there long enough to stop being interesting. That stillness is the difference between a joke about failure and a joke about acceptance. The shirt is not angry about the dashed circle. It is wearing it.

What to take from the drawing

The next time you sketch a diagram — a flow, a budget, a state machine, a before-and-after — look at which parts you have drawn solid and which parts you have left dashed. The dashed parts are the claims you have not tested. The solid parts are the ones you have. A diagram that shows both, without labeling which is which, is asking the reader to do the work of telling them apart. That is what this shirt does, and it is why the joke survives more than one look.

The concrete technique is not about React, and it is not about the shirt’s fabric or fit. It is about the drawing: when you present a system, mark the occupied states and the hypothetical ones with different line weights, and do not caption the difference. The reader who notices will trust the diagram more. The reader who does not will still see the shape. Either way, the dashed circle is doing its job.

Editorial note: This publication and Quirky Cotton Collective share an owner.

The shirt is available at the Amazon listing.

The Period After ‘meh’ Is the Whole Design

Editorial note: This publication and Quirky Cotton Collective share an owner.

The garment preview shows four lowercase letters — meh. — set large and bold in white against a dark shirt. The letters are heavy enough to read across a room. The word they spell is the sound of someone declining to have an opinion. That mismatch is the entire design, and it is worth taking seriously for a minute, because it is a small lesson in how typography carries meaning that the words alone do not.

Most slogan shirts fail by overexplaining. They stack a setup, a punchline, and a parenthetical, and the wearer becomes a billboard for a joke that has already been told. This one does the opposite. It gives you one word and one punctuation mark, and it trusts you to supply the tone. The boldness is not decoration; it is the joke. A whisper set in 96-point type is funnier than a shout set in 12-point, because the form contradicts the content. The shirt is loud about being unimpressed.

That is a real typographic principle, not a marketing claim. Weight and scale signal emphasis. When you apply maximum emphasis to a word that means no strong feeling either way, you create a deliberate friction. The eye reads the size first and expects a declaration. The brain reads the word second and gets a shrug. The half-second of confusion is the payload. Designers use this trick constantly — giant type for small ideas, quiet type for big ones — and it works because readers process visual weight before they process meaning.

The period matters too. meh on its own is a sound. meh. with a full stop is a verdict. The period closes the conversation. It is the typographic equivalent of a door clicking shut, politely but firmly. Without it, the shirt would be ambient mood. With it, the shirt is a complete sentence and a complete refusal. That single mark is doing more work than most slogans manage in a paragraph.

There is also the question of what is not there. The preview shows no animal, no mascot, no extra illustration, no secondary text. The design resists the urge to add a second joke. That restraint is what keeps the word legible at distance and keeps the wearer from becoming a costume. A shirt that says one thing clearly is easier to wear repeatedly than a shirt that says five things at once. The absence of clutter is a feature, not a gap.

For a certain kind of reader — the one who spends the day in code review, incident channels, and planning meetings — the word has a specific utility. meh is the honest response to a proposal that is neither good nor bad, a meeting that could have been an email, a refactor that solves a problem nobody had. It is a boundary word. It says I have considered this and I do not have the energy to pretend enthusiasm. Wearing it is a low-stakes way of signaling that you have opinions but you are rationing them. That is a recognizable posture in technical work, where performative excitement is often less useful than a flat, accurate assessment.

I want to be careful here. The shirt does not demonstrate anything about engineering, and it does not prove a point about workplace culture. It is a piece of clothing with a word on it. But the word is well chosen for people who value terse, accurate communication, and the typography is well chosen for people who notice when form and content disagree on purpose. That is a legitimate reason to like an object, and it is a more interesting reason than it is funny.

If you are considering the meh shirt, the thing to evaluate is not whether the joke lands in the abstract. It is whether you want to wear a design that is loud in form and quiet in content. The bold white lowercase lettering on a dark ground is high contrast and reads cleanly; the single period gives it a finished, deliberate look. It is a simple composition executed with enough confidence to leave it alone. That is harder than it sounds, and it is the reason the design works.

The practical takeaway, if you want one: when you are writing a message that needs to land — a status update, a commit message, a one-line README — pay attention to the relationship between how it looks and what it says. Emphasis applied to the wrong word is noise. Emphasis applied to a word that means the opposite of emphasis is a joke. Knowing which one you are making is most of the craft.