All Components

Newsletter CTA

Ever seen those newsletter signups that make you want to click "Maybe later" faster than you can say "spam"? Well, this one's different! It's like that friendly barista who remembers your order - smooth, welcoming, and not at all pushy.

What's Cooking?

  • A primary button that's more tempting than free pizza
  • A "Maybe later" button that slides away like it's late for a meeting
  • A sneaky message that appears when you hover (like that friend who always has an opinion)
  • Works on all devices (even your grandma's flip phone)
  • Animations smoother than a buttered pancake

The magic happens with AnimatePresence - it's like having a personal animator for your buttons, making everything flow like a well-choreographed dance.

"use client";

import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { twMerge } from "tailwind-merge";

export default function NewsletterCTA() {
const [isHovered, setIsHovered] = useState(false);

return (
  <div className="w-full max-w-md">
    <div
      className="flex items-center w-full text-sm md:text-base ring-1 ring-neutral-800 hover:ring-neutral-700 p-1 rounded-xl overflow-hidden"
      onMouseLeave={() => setIsHovered(false)}
    >
      {/* Primary Button */}
      <button
        className={twMerge(
          "flex-1 bg-gradient-to-br from-blue-800 to-blue-400 font-semibold p-2 rounded-lg",
          isHovered ? "mr-0" : "mr-2"
        )}
      >
        <p className="text-white">Join Newsletter!</p>
      </button>

      {/* Not Interested Button */}
      <AnimatePresence initial={false}>
        {!isHovered && (
          <motion.button
            key="not-interested"
            className="rounded-lg bg-gray-200 font-semibold py-2 overflow-hidden whitespace-nowrap"
            initial={{ opacity: 0, x: 15, width: 0 }}
            animate={{ opacity: 1, x: 0, width: 120 }}
            exit={{ opacity: 0, x: 15, width: 0 }}
            onMouseEnter={() => setIsHovered(true)}
          >
            <p className="text-black">Maybe later</p>
          </motion.button>
        )}
      </AnimatePresence>
    </div>

    <div className="h-[10px] mt-2">
      <AnimatePresence>
        {isHovered && (
          <motion.p
            initial={{ opacity: 0, y: 5, filter: "blur(4px)" }}
            animate={{ opacity: 1, y: 0, filter: "blur(0)" }}
            exit={{ opacity: 0, y: 5, filter: "blur(4px)" }}
            transition={{ duration: 0.25 }}
            className="text-xs text-neutral-500"
          >
            Just join you&apos;ll not regret!
          </motion.p>
        )}
      </AnimatePresence>
    </div>
  </div>
);
}

Happy Coding 👋

Want to Learn How to Build These Components Yourself?

We have step-by-step tutorials for every component to help you create stunning interfaces with ease.

Tutorials on YouTube