This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry.
There is a specific kind of bug that makes you question whether you know how to code at all.
Not the kind where something throws a clear error and you trace it back in two minutes.
The kind where the entire browser window starts flashing black and the only option is to kill the tab before Chrome locks up completely.
That was the loading screen bug in my portfolio.
The project I had just migrated my personal portfolio to Next.js 16 and was building a more complete version from scratch.
The site had a custom loading screen mounted in the root layout.
It needed to be there because it was the first thing users saw before any content loaded.
The loader is not a simple spinner.
It draws an SVG path that traces my initial letter G using a stroke animation, moves it to the left, reveals my full name alongside it, and then executes a color wipe exit animation in two stages before calling an callback and unmounting.
There are refs for direct DOM manipulation of the SVG elements, state variables for the wipe stages, timers, and transition logic layered across all of it.
Standard stuff.
Or so I thought.
What was happening The moment I opened the dev server, the browser window started flashing.
Not a subtle flicker.
The entire viewport alternating between black and content at full speed, over and over, with no way to stop it without closing the tab.
Within seconds, Chrome would throw errors and warnings in the console and start slowing down noticeably.
If I left it running, the browser would eventually saturate completely.
The same thing happened in production.
The page stayed black.
The loop was executing so fast that the content never had time to render visibly.
Anyone visiting the site would see a black screen and nothing else.
I had to kill the local server every time I opened it just to be able to work on anything else in the project.
Why it took time to find The symptom was so extreme that it pointed in the wrong direction.
When a browser behaves like that, the instinct is to look for something major: a memory leak, a broken build configuration, a dependency conflict.
I spent time checking all of those and found nothing.
The component itself looked plausible when I read through it.
There was state, there were refs, there was timer logic.
No obvious loop anywhere in the code.
What eventually led me to the actual cause was a combination of two things.
I found posts on DEV describing similar symptoms, which pointed me toward infinite render loops as the likely category of problem.
Then I opened React DevTools and looked at what was actually happening at runtime.
The console was showing: That error, combined with watching the component re-mount continuously in the DevTools component tree, made the cause clear.
The problem was that state updates controlling the loader behavior were running during render, outside of any effect.
In a component with this much timer and transition logic, that is easy to do accidentally.
Every render triggered a state change, which triggered another render, which triggered another state change.
The browser was re-rendering the entire root layout hundreds of times per second, which is why the viewport was flashing and everything eventually locked up.
The complexity of the component made it harder to catch on a read-through because the timer logic created the impression that the state updates were conditional and time-bounded.
They were not.
The condition was being evaluated on every render.
The fix The solution was two things applied together.
First, separating the SVG animation logic and the wipe exit logic into two distinct hooks, each with explicit cleanup.
The SVG animation runs once after mount.
The wipe timers run in a separate effect that cleans up all three timeouts on unmount.
Second, I moved the loader into its own isolated component instead of keeping it inline in the root layout.
That separation made the lifecycle pred