Ring Button

A button with a ring and scale effect on hover.

Preview

Code

Code

app/page.tsx

1import RingButton from "@/app/components/buttons/ring-button"; 2 3const HomePage = () => { 4 return ( 5 <div className="w-screen h-screen bg-white items-center justify-center flex"> 6 <RingButton text="Astrae's Amazing" /> 7 </div> 8 ); 9} 10 11export default HomePage; 12

Installation

Initialized a project and installed the necessary dependencies to use this component.

1. Initialize a new Next.js project:

Code
npx create-next-app@latest my-app

2. Install Framer Motion

Code
npm install framer-motion

Copy & Paste Components

Code

components/buttons/ring-button.tsx

1"use client" 2 3import { motion } from "framer-motion"; 4import { useState } from "react"; 5 6const RingButton = ({ text, fullWidth = false }: { text: string; fullWidth?: boolean }) => { 7 const [isHovered, setIsHovered] = useState(false) 8 9 const containerVariants = { 10 hidden: { opacity: 1 }, 11 visible: { 12 opacity: 1, 13 transition: { 14 staggerChildren: 0.015, 15 }, 16 }, 17 exit: { 18 opacity: 1, 19 transition: { 20 staggerChildren: 0.015, 21 }, 22 }, 23 } 24 25 const charVariants = { 26 hidden: { 27 y: 0, 28 }, 29 visible: { 30 y: -30, 31 transition: { 32 duration: 0.25, 33 ease: [0.25, 0.46, 0.45, 0.94] as const, 34 }, 35 }, 36 exit: { 37 y: 2, 38 transition: { 39 duration: 0.25, 40 ease: [0.25, 0.46, 0.45, 0.94] as const, 41 }, 42 }, 43 } 44 45 const charVariants2 = { 46 hidden: { 47 y: 30, 48 }, 49 visible: { 50 y: 0, 51 transition: { 52 duration: 0.25, 53 ease: [0.25, 0.46, 0.45, 0.94] as const, 54 }, 55 }, 56 exit: { 57 y: 30, 58 transition: { 59 duration: 0.25, 60 ease: [0.25, 0.46, 0.45, 0.94] as const, 61 }, 62 }, 63 } 64 65 return ( 66 <motion.button 67 className={`rounded-full flex items-center justify-center cursor-pointer 68 bg-[#002BBA] px-4 md:px-5 h-10 text-sm md:text-[15px] text-white 69 hover:ring-2 hover:ring-[#002BBA]/70 ring-offset-2 ring-offset-white 70 transition-all hover:scale-[1.02] ring-transparent 71 active:scale-[0.98] active:ring-[#002BBA] overflow-hidden 72 relative ${fullWidth ? 'w-full' : ''}`} 73 onMouseEnter={() => setIsHovered(true)} 74 onMouseLeave={() => setIsHovered(false)} 75 whileTap={{ scale: 0.98 }} 76 > 77 <span className="relative inline-block h-[1.6em] overflow-hidden"> 78 <motion.span 79 className="inline-flex" 80 variants={containerVariants} 81 initial="hidden" 82 animate={isHovered ? "visible" : "exit"} 83 > 84 {text.split("").map((char, index) => ( 85 <motion.span 86 key={`first-${char}-${index}`} 87 variants={charVariants} 88 className="inline-block" 89 style={{ 90 display: char === " " ? "inline" : "inline-block", 91 whiteSpace: "pre" 92 }} 93 > 94 {char} 95 </motion.span> 96 ))} 97 </motion.span> 98 99 <motion.span 100 className="inline-flex absolute inset-0 items-center justify-center" 101 variants={containerVariants} 102 initial="hidden" 103 animate={isHovered ? "visible" : "exit"} 104 > 105 {text.split("").map((char, index) => ( 106 <motion.span 107 key={`second-${char}-${index}`} 108 variants={charVariants2} 109 className="inline-block" 110 style={{ 111 display: char === " " ? "inline" : "inline-block", 112 whiteSpace: "pre" 113 }} 114 > 115 {char} 116 </motion.span> 117 ))} 118 </motion.span> 119 </span> 120 </motion.button> 121 ) 122} 123 124export default RingButton

Become an Astrae
Affiliate Today

Make referrals, and bring in clients. Keep 50% of your earnings paid out weekly.

Description