<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christopher Cameron</title>
    <description>The latest articles on DEV Community by Christopher Cameron (@christopher_cameron_e7fce).</description>
    <link>https://dev.to/christopher_cameron_e7fce</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3802036%2F587ee7f0-8dc6-4907-b9bf-c952b578e729.jpg</url>
      <title>DEV Community: Christopher Cameron</title>
      <link>https://dev.to/christopher_cameron_e7fce</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christopher_cameron_e7fce"/>
    <language>en</language>
    <item>
      <title>Stop Murdering Your React App: A Buttery Smooth Scroll-To-Top Component</title>
      <dc:creator>Christopher Cameron</dc:creator>
      <pubDate>Mon, 02 Mar 2026 16:58:52 +0000</pubDate>
      <link>https://dev.to/christopher_cameron_e7fce/stop-murdering-your-react-app-a-buttery-smooth-scroll-to-top-component-1lg7</link>
      <guid>https://dev.to/christopher_cameron_e7fce/stop-murdering-your-react-app-a-buttery-smooth-scroll-to-top-component-1lg7</guid>
      <description>&lt;p&gt;We’ve all been there. You’re reading a brilliant, massive piece of content, you hit the bottom, and suddenly you have to manually scroll all the way back up like it’s 2004. &lt;/p&gt;

&lt;p&gt;Dropping a "Scroll to Top" button into your React app is a no-brainer for user experience. But if you aren't careful, that tiny little convenience button can absolutely tank your app's performance. &lt;/p&gt;

&lt;p&gt;Today, I’m going to share a highly optimized, buttery smooth &lt;code&gt;ScrollToTop&lt;/code&gt; component that rescues your users without causing a chaotic avalanche of React re-renders. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Trap: Tracking Every Single Pixel
&lt;/h2&gt;

&lt;p&gt;When I first built one of these, I fell into the classic trap: storing the exact &lt;code&gt;window.scrollY&lt;/code&gt; position in React state. &lt;/p&gt;

&lt;p&gt;Here is the brutal reality of that approach: every single pixel your user scrolls triggers a state update. If they scroll down a long page, you are forcing your component to re-render hundreds of times a second. It’s a massive resource hog for absolutely no reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Threshold State &amp;amp; CSS Wizardry
&lt;/h2&gt;

&lt;p&gt;Instead of tracking the exact scroll depth, we only care about one thing: &lt;em&gt;Has the user scrolled past our threshold?&lt;/em&gt; (In this case, 300px). We track a single boolean value, meaning React only re-renders twice—once when the button appears, and once when it vanishes.&lt;/p&gt;

&lt;p&gt;Here is the optimized component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState, useEffect } from 'react';
import './ScrollToTop.css';

const ScrollToTop = () =&amp;gt; {
    const [isVisible, setIsVisible] = useState(false);

    useEffect(() =&amp;gt; {
        const handleScroll = () =&amp;gt; {
            // We only toggle state when crossing the 300px mark
            if (window.scrollY &amp;gt; 300) {
                setIsVisible(true);
            } else {
                setIsVisible(false);
            }
        };

        // The { passive: true } option is crucial for scroll performance!
        window.addEventListener('scroll', handleScroll, { passive: true });

        return () =&amp;gt; {
            window.removeEventListener('scroll', handleScroll);
        };
    }, []); // Empty array ensures this listener only mounts once

    const handleClick = () =&amp;gt; {
        window.scrollTo({
            top: 0,
            behavior: 'smooth'
        });
    };

    return (
        &amp;lt;div className='scroll-to-top-button-div'&amp;gt;
            &amp;lt;button 
                className={`btn ${isVisible ? 'show' : 'hide'}`} 
                type='button' 
                onClick={handleClick}
                aria-label="Scroll to top"
            &amp;gt;
                Scroll to Top &amp;amp;uArr;
            &amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    );
};

export default ScrollToTop;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Styling: Ditch&lt;/strong&gt; display: none&lt;/p&gt;

&lt;p&gt;You cannot animate an element that toggles between display: block and display: none. It just violently snaps in and out of existence. To get that buttery smooth fade, we use opacity and visibility instead, pairing it with pointer-events: none so the invisible button doesn't block underlying clicks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.btn {
    padding: 12px 20px;
    border: none;
    border-radius: 8px;
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1000;
    background-color: slateblue;
    color: white;
    cursor: pointer;
    /* Transition opacity and visibility for a smooth fade */
    transition: opacity 0.4s ease, visibility 0.4s ease, transform 0.3s ease;
    box-shadow: 0 4px 6px rgba(0,0,0,0.2);
}

.btn:hover {
    background-color: orange;
    transform: translateY(-3px);
}

.show {
    opacity: 0.85;
    visibility: visible;
}

.hide {
    opacity: 0;
    visibility: hidden;
    pointer-events: none; /* Prevents invisible clicks */
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why This Component is a Winner&lt;br&gt;
Zero Drag: By ditching pixel-tracking, your browser can breathe easy.&lt;/p&gt;

&lt;p&gt;Passive Listeners: Adding { passive: true } tells the browser that our event listener won't prevent the default scroll behaviour, keeping the scroll itself perfectly smooth.&lt;/p&gt;

&lt;p&gt;Accessible &amp;amp; Clean: The aria-label keeps it friendly for screen readers, and the CSS animations make it feel like a premium UI feature.&lt;/p&gt;

&lt;p&gt;Feel free to snag this for your own projects! Let me know in the comments how you handle scroll events in your React builds—are there any custom hooks you prefer over a standard useEffect?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
