Auto-hiding Sticky Navigation With Framer Motion
![](/video-images/sticky-navigation.webp)
Watch tutorial
With CSS position sticky
, making a sticky navigation has become super easy. But what if you want to hide the navigation when the user scrolls down and show it when the user scrolls up? This is a common pattern that is actually a little bit harder to achieve.
In this recipe we’re using Framer Motion’s useScroll
hook to get the current scroll position, and toggle the state of the navigation based on the scroll direction.
Watch the tutorial linked at the top of this recipe if you want to see how it’s done step by step.
The code
1import { motion, useScroll, useMotionValueEvent } from "framer-motion";2import { useRef, useState } from "react";3
4function App() {5 return (6 <div className="min-h-[300vh] bg-gradient-to-b from-[rgba(255,255,255,.1)] to-[rgba(255,255,255,0)]">7 <Nav />8 </div>9 );10}11
12const Nav = () => {13 const [isHidden, setIsHidden] = useState(false);14 const { scrollY } = useScroll();15 const lastYRef = useRef(0);16
17 useMotionValueEvent(scrollY, "change", (y) => {18 const difference = y - lastYRef.current;19 if (Math.abs(difference) > 50) {20 setIsHidden(difference > 0);21
22 lastYRef.current = y;23 }24 });25
26 return (27 <motion.div28 animate={isHidden ? "hidden" : "visible"}29 whileHover="visible"30 onFocusCapture={() => setIsHidden(false)}31 variants={{32 hidden: {33 y: "-90%",34 },35 visible: {36 y: "0%",37 },38 }}39 transition={{ duration: 0.2 }}40 className="fixed top-0 z-10 flex w-full justify-center pt-3"41 >42 <nav className="flex justify-between gap-3 rounded-3xl bg-white p-5 *:rounded-xl *:border *:border-gray-200 *:px-7 *:py-2 *:transition-colors *:duration-300 hover:*:bg-gray-200 focus-visible:*:bg-gray-200">43 <a href="#" className="bg-gray-200">44 <svg45 className="h-6 w-6"46 fill="currentColor"47 viewBox="0 0 24 24"48 xmlns="http://www.w3.org/2000/svg"49 >50 <path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5-10-5-10 5z"></path>51 </svg>52 <span className="sr-only">Home</span>53 </a>54 <a href="#">Products</a>55 <a href="#">Services</a>56 <a href="#">About</a>57 <a href="#">Contact</a>58 </nav>59 </motion.div>60 );61};62
63export default App;
Changelog
- 2024-06-06: Added the initial version of the component.
- 2024-06-08: Updated snippet to match end result of tutorial: No additional (unnecessary) peeking variant.