Floating Button
A floating button is a common UI component used in user interfaces. It typically appears as a button "floating" over other content, often positioned in a corner of the screen. This button allows users to quickly perform a key action. It is also known as a Floating Action Button (FAB).
Installation
Install the following dependencies:
npm i framer-motion clsx tailwind-merge usehooks-ts
Add util file
import { ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Copy and paste the following code into your project.
'use client';
import { ReactNode, useRef, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { useOnClickOutside } from 'usehooks-ts';
type FloatingButtonProps = {
className?: string;
children: ReactNode;
triggerContent: ReactNode;
};
type FloatingButtonItemProps = {
children: ReactNode;
};
const list = {
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
staggerDirection: -1
}
},
hidden: {
opacity: 0,
transition: {
when: 'afterChildren',
staggerChildren: 0.1
}
}
};
const item = {
visible: { opacity: 1, y: 0 },
hidden: { opacity: 0, y: 5 }
};
const btn = {
visible: { rotate: '45deg' },
hidden: { rotate: 0 }
};
function FloatingButton({ className, children, triggerContent }: FloatingButtonProps) {
const ref = useRef(null);
const [isOpen, setIsOpen] = useState(false);
useOnClickOutside(ref, () => setIsOpen(false));
return (
<div className="flex flex-col items-center relative">
<AnimatePresence>
<motion.ul
className="flex flex-col items-center absolute bottom-14 gap-2"
initial="hidden"
animate={isOpen ? 'visible' : 'hidden'}
variants={list}>
{children}
</motion.ul>
<motion.div
variants={btn}
animate={isOpen ? 'visible' : 'hidden'}
ref={ref}
onClick={() => setIsOpen(!isOpen)}>
{triggerContent}
</motion.div>
</AnimatePresence>
</div>
);
}
function FloatingButtonItem({ children }: FloatingButtonItemProps) {
return <motion.li variants={item}>{children}</motion.li>;
}
export { FloatingButton, FloatingButtonItem };
Usage
import { FloatingButton, FloatingButtonItem } from '@/components/ui/floating-button';
import { cn } from '@/lib/utils';
import { DribbbleIcon, FacebookIcon, LinkedinIcon, PlusIcon } from 'lucide-react';
export default function FloatingButtonExample() {
const items = [
{
icon: <FacebookIcon />,
bgColor: 'bg-[#1877f2]'
},
{
icon: <DribbbleIcon />,
bgColor: 'bg-[#ea4c89]'
},
{
icon: <LinkedinIcon />,
bgColor: 'bg-[#0a66c2]'
}
];
return (
<FloatingButton
triggerContent={
<button className="flex items-center justify-center h-12 w-12 rounded-full bg-black dark:bg-slate-800 text-white/80 z-10">
<PlusIcon />
</button>
}>
{items.map((item, key) => (
<FloatingButtonItem key={key}>
<button
className={cn(
'h-12 w-12 rounded-full flex items-center justify-center text-white/80',
item.bgColor
)}>
{item.icon}
</button>
</FloatingButtonItem>
))}
</FloatingButton>
);
}