I'm making a react app with tailwindcss
, and I want to make a hidden mobile navbar and when the user click on the icon it appears.
So I want to make a transition while the menu appears.
I use:
My Code:
MobileMenu.js:
function MobileMenu() {
return (
<div className="block md:hidden px-4 py-3 text-white w-full bg-gray-800 border-t border-opacity-70 border-slate-700">
<div className="flex items-center mb-3 pb-3 border-b border-slate-700">
<img
src="https://africaprime.com/wp-content/uploads/2020/04/ElonMusk.jpg"
className="rounded-full w-8 h-8 cursor-pointer"
/>
<h6 className="ml-5 cursor-pointer">Elon Musk</h6>
</div>
<div className="mobile-nav-icon">
<ImHome size={20} />
<h4 className="ml-5">Home</h4>
</div>
<div className="mobile-nav-icon">
<HiUsers size={20} />
<h4 className="ml-5">Friends</h4>
</div>
<div className="mobile-nav-icon">
<CgProfile size={20} />
<h4 className="ml-5">My Profile</h4>
</div>
</div>
);
}
export default MobileMenu;
How I show it in Navbar.js:
function Navbar() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return (
<>
<nav className="flex justify-between items-center px-4 lg:px-8 py-3 bg-gray-900 text-white">
{/* Mobile Menu Icon */}
<div
className="block md:hidden p-2 cursor-pointer rounded-full hover:bg-gray-700 transition-2"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
<FiMenu size={20} />
</div>
</nav>
{/* Mobile Menu */}
{mobileMenuOpen && <MobileMenu />}
</>
);
}
export default Navbar;
Thanks in advance!
Your approach is to include or exclude the whole component depending on the visibility state, but a DOM structure change can't be animated. The right approach is to pick a CSS property of an existing element that can change gradually over time, like position and opacity. This solution uses only Tailwind means, no additional dependency for animation required.
As for TailwindCSS, the fundamental mechanics is to declare that CSS changes animate as transitions. Add the transition
class for any change to animate, or pick out specific properties with transition-opacity
or transition-transform
. (The latter for scale, translate, rotate transformations.) Optionally, specify the duration time with duration-300
, if the default of 150 doesn't fit.
The actual transition animation is kicked off by changing CSS properties, which means in Tailwind adding, removing, or replacing one or more classes.
This below is how this might look like. The code toggles animated properties based on the new passed-in prop visible
, which comes from Navbar
and its open button. Both opacity and a translation are animated as transitions. ease-out
and ease-in
are flipped as well depending on if the transition is going in or out.
// MobileMenu.js
import { ImHome } from "react-icons/im";
import { CgProfile } from "react-icons/cg";
import { HiUsers } from "react-icons/hi";
import { clsx } from "clsx";
function MobileMenu({ visible }) {
return (
<div
className={clsx(
"relative block px-4 py-3 text-white w-full bg-gray-800 border-t border-opacity-70 border-slate-700 flex flex-col space-y-5",
"transition duration-200 -z-10 ease-out",
{
"ease-out": visible,
"ease-in": !visible,
"opacity-0": !visible,
"opacity-100": visible,
"-translate-y-full": !visible,
"translate-y-0": visible,
}
)}
>
<div className="flex items-center pb-3 border-b border-slate-700">
<img
src="https://placehold.co/32x32"
className="rounded-full w-8 h-8 cursor-pointer"
/>
<h6 className="ml-5 cursor-pointer">Elon Musk</h6>
</div>
<div className="flex items-center">
<ImHome size={20} />
<h4 className="ml-5">Home</h4>
</div>
<div className="flex items-center">
<HiUsers size={20} />
<h4 className="ml-5">Friends</h4>
</div>
<div className="flex items-center">
<CgProfile size={20} />
<h4 className="ml-5">My Profile</h4>
</div>
</div>
);
}
export default MobileMenu;
// Navbar.js
import React, { useState } from "react";
import { FiMenu } from "react-icons/fi";
import MobileMenu from "./MobileMenu";
function Navbar() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return (
<>
<nav className="relative flex justify-between items-center px-4 lg:px-8 py-3 bg-gray-900 text-white z-10">
{/* Mobile Menu Icon */}
<div
className="block md:hidden p-2 cursor-pointer rounded-full hover:bg-gray-700"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
<FiMenu size={20} />
</div>
</nav>
{/* Mobile Menu */}
<MobileMenu visible={mobileMenuOpen} />
</>
);
}
export default Navbar;
Check out the running code as codesandbox.