This content originally appeared on DEV Community and was authored by Sushil
Animations aren’t just for fancy websites or landing pages. Even subtle, simple animations can make your site feel smoother, more polished, and more enjoyable to use.
In this post, I’ll show you how I used basic animations with Framer Motion and React to create a smooth experience on a component I call Smooth Reveal.
Live Demo
I made a small demo to show this in action.
Check it out here
What’s Inside the Smooth Reveal Component?
Here’s what happens in the demo:
- Navbar fades in from the top.
- Main section smoothly reveals the text WI and LD, with a video expanding in the center.
- Footer slides in with content and a CTA button.
Full Code Overview
-
Navbar.tsx
A simple navbar with animated hover effect and theme switcher:
"use client";
import Link from "next/link";
import React, { useState } from "react";
import { motion } from "motion/react";
import MotionDiv from "@/components/animation/motion-div";
import { MoonIcon, SunIcon } from "lucide-react";
import { useTheme } from "next-themes";
const links = [
{ name: "Home", href: "/" },
{ name: "About", href: "/about" },
{ name: "Contact", href: "/contact" },
];
const Navbar = () => {
const [isHovered, setIsHovered] = useState<number | null>(null);
const { theme, setTheme } = useTheme();
return (
<MotionDiv
initial={{ y: -10, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.5, delay: 0.6 }}
className="flex w-full justify-between sm:h-10"
>
<div className="flex flex-col items-start sm:flex-row sm:items-center">
{links.map((link, idx) => (
<Link
key={idx}
href={link.href}
className="relative flex gap-1 px-2.5 py-1.5 text-[14px] sm:px-4.5"
onMouseEnter={() => setIsHovered(idx)}
onMouseLeave={() => setIsHovered(null)}
>
{isHovered === idx && (
<motion.span
layoutId="hovered-span"
className="absolute inset-0 h-full w-full rounded-2xl bg-neutral-900 backdrop-blur-sm dark:bg-neutral-100"
transition={{ duration: 0.3 }}
/>
)}
<span className={`relative z-10 font-medium transition-colors duration-300 ${
isHovered === idx
? "text-white dark:text-black"
: "text-black dark:text-white"
}`}>
{link.name}
</span>
</Link>
))}
</div>
<div className="flex items-start justify-start gap-3 sm:items-center sm:gap-4">
<Link
href="/login"
className="hidden items-center justify-center rounded-xl bg-neutral-200 px-4 py-1.5 text-sm font-medium text-neutral-900 hover:bg-neutral-300 sm:flex sm:px-6"
>
Login
</Link>
<Link
href="/signup"
className="hidden items-center justify-center rounded-xl bg-neutral-800 px-4 py-1.5 text-sm font-medium text-neutral-100 hover:bg-neutral-700 sm:flex sm:px-6"
>
Sign Up
</Link>
<motion.button
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="ml-1 rounded-full border p-2 text-neutral-900 hover:bg-neutral-100 dark:text-neutral-100 hover:dark:bg-neutral-800"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<motion.span
key={theme}
initial={{ rotate: -180, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
transition={{ duration: 0.3 }}
className="flex size-4 items-center justify-center"
>
{theme === "dark" ? <MoonIcon /> : <SunIcon />}
</motion.span>
</motion.button>
</div>
</MotionDiv>
);
};
export default Navbar;
-
Main.tsx
The central text and animated video reveal section:
"use client";
import React, { useEffect, useState } from "react";
import { motion } from "framer-motion";
const Main = () => {
const [isSmallScreen, setIsSmallScreen] = useState(false);
useEffect(() => {
const handleResize = () => {
setIsSmallScreen(window.innerWidth < 640);
};
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return (
<div className="flex h-full w-full items-center justify-center text-[24vw] font-semibold text-neutral-900 md:text-[18vw] xl:text-[14vw] dark:text-white">
<div className="flex w-full flex-col items-center sm:flex-row sm:justify-center">
<motion.span
initial={{ opacity: 0, x: -50, scale: 0.9 }}
animate={{ opacity: 1, x: 0, scale: 1 }}
transition={{ duration: 0.8, delay: 0.1 }}
>
WI
</motion.span>
<motion.div
initial={{ width: "0px" }}
animate={{ width: isSmallScreen ? "90%" : "35%" }}
transition={{ duration: 0.7, delay: 0.3 }}
className="mx-2 overflow-hidden rounded-full"
>
<motion.video
initial={{ width: "0px" }}
animate={{ width: "100%" }}
transition={{ duration: 0.5, delay: 0.4 }}
autoPlay
muted
loop
playsInline
className="h-full w-full object-cover"
>
<source src="/playground/car-shot.mp4" type="video/mp4" />
</motion.video>
</motion.div>
<motion.span
initial={{ opacity: 0, x: 50, scale: 0.9 }}
animate={{ opacity: 1, x: 0, scale: 1 }}
transition={{ duration: 0.8, delay: 0.1 }}
>
LD
</motion.span>
</div>
</div>
);
};
export default Main;
-
Footer.tsx
Simple fade-in footer with a message and CTA button:
import MotionDiv from "@/components/animation/motion-div";
import React from "react";
const Footer = () => {
return (
<MotionDiv
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.6 }}
className="flex w-full flex-col items-start justify-between gap-4 px-4 sm:flex-row sm:items-center sm:gap-0"
>
<p className="text-xs leading-relaxed text-neutral-400 sm:max-w-md sm:text-sm lg:max-w-5xl">
Discover extraordinary experiences that push the boundaries of innovation. Our platform connects visionaries with cutting-edge solutions, turning ideas into reality through seamless collaboration.
</p>
<button className="mb-4 rounded-xl bg-neutral-800 px-4 text-sm font-medium text-white hover:bg-neutral-700 sm:mb-0 sm:px-6 sm:py-2 dark:bg-neutral-100 dark:text-black">
Explore More
</button>
</MotionDiv>
);
};
export default Footer;
-
SmoothReveal.tsx
Combining everything into one complete component:
import React from "react";
import Navbar from "./navbar";
import Main from "./main";
import Footer from "./footer";
const SmoothReveal = () => {
return (
<div className="hide-scrollbar relative flex h-[85vh] w-full flex-col items-center justify-between rounded-2xl bg-neutral-50 p-4 dark:bg-neutral-900">
<div className="absolute inset-0 h-full w-full bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:72px_72px]" />
<div className="z-10 flex h-full w-full flex-col items-center justify-between">
<Navbar />
<Main />
<Footer />
</div>
</div>
);
};
export default SmoothReveal;
Final Thoughts
You don’t need overly complex animation libraries to make your website feel smooth and modern. Just a few well-placed transitions can make a huge difference.
Start small, stay consistent, and build from there.
Live Demo – Smooth Reveal
Check out my full Portfolio
Follow me on Twitter for more frontend experiments and project updates.
Let me know what you think or share how you’re using animation in your own projects!
This content originally appeared on DEV Community and was authored by Sushil