Components
Floating Action Button
Floating Action Button
Expandable floating action button with ripple effects, tooltips, and smooth spring animations.
Installation
1
Install the packages
npm i motion clsx tailwind-merge2
Add util file
lib/util.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
3
Copy and paste the following code into your project
floating-action-button.tsx
"use client";
import { motion } from "motion/react";
import { useState } from "react";
import { cn } from "@/lib/utils";
import { FiPlus, FiEdit, FiShare } from "react-icons/fi";
type FloatingActionButtonProps = {
size?: number;
expanded?: boolean;
};
const FloatingActionButton = ({
size = 56,
expanded: initialExpanded = false,
}: FloatingActionButtonProps) => {
const [isExpanded, setIsExpanded] = useState(initialExpanded);
const actions = [
{ icon: FiEdit, label: "Edit", color: "bg-blue-500" },
{ icon: FiShare, label: "Share", color: "bg-green-500" },
];
return (
<div className="relative">
{/* Main FAB */}
<motion.button
className={cn(
"rounded-full bg-blue-500 text-white shadow-lg flex items-center justify-center",
"hover:bg-blue-600 transition-colors duration-200"
)}
style={{ width: size, height: size }}
onClick={() => setIsExpanded(!isExpanded)}
whileTap={{ scale: 0.95 }}
animate={{ rotate: isExpanded ? 45 : 0 }}
transition={{ duration: 0.2 }}
>
<FiPlus className="text-xl" />
</motion.button>
{/* Expanded actions */}
{isExpanded && (
<motion.div
className="absolute bottom-full mb-4 flex flex-col gap-3"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.2 }}
>
{actions.map((action, index) => {
const Icon = action.icon;
return (
<motion.button
key={action.label}
className={cn(
"rounded-full text-white shadow-lg flex items-center justify-center",
"hover:scale-110 transition-transform duration-200",
action.color
)}
style={{ width: size * 0.8, height: size * 0.8 }}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1, duration: 0.3 }}
whileTap={{ scale: 0.9 }}
>
<Icon className="text-lg" />
</motion.button>
);
})}
</motion.div>
)}
</div>
);
};
export default FloatingActionButton;"use client";
import { motion } from "motion/react";
import { useState } from "react";
import { cn } from "@/lib/utils";
import { FiPlus, FiEdit, FiShare } from "react-icons/fi";
type FloatingActionButtonProps = {
size?: number;
expanded?: boolean;
};
const FloatingActionButton = ({
size = 56,
expanded: initialExpanded = false,
}: FloatingActionButtonProps) => {
const [isExpanded, setIsExpanded] = useState(initialExpanded);
const actions = [
{ icon: FiEdit, label: "Edit", color: "bg-blue-500" },
{ icon: FiShare, label: "Share", color: "bg-green-500" },
];
return (
<div className="relative">
{/* Main FAB */}
<motion.button
className={cn(
"rounded-full bg-blue-500 text-white shadow-lg flex items-center justify-center",
"hover:bg-blue-600 transition-colors duration-200"
)}
style={{ width: size, height: size }}
onClick={() => setIsExpanded(!isExpanded)}
whileTap={{ scale: 0.95 }}
animate={{ rotate: isExpanded ? 45 : 0 }}
transition={{ duration: 0.2 }}
>
<FiPlus className="text-xl" />
</motion.button>
{/* Expanded actions */}
{isExpanded && (
<motion.div
className="absolute bottom-full mb-4 flex flex-col gap-3"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.2 }}
>
{actions.map((action, index) => {
const Icon = action.icon;
return (
<motion.button
key={action.label}
className={cn(
"rounded-full text-white shadow-lg flex items-center justify-center",
"hover:scale-110 transition-transform duration-200",
action.color
)}
style={{ width: size * 0.8, height: size * 0.8 }}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1, duration: 0.3 }}
whileTap={{ scale: 0.9 }}
>
<Icon className="text-lg" />
</motion.button>
);
})}
</motion.div>
)}
</div>
);
};
export default FloatingActionButton;4
Update the import paths to match your project setup
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| size | number | 56 | The size of the FAB in pixels. |
| expanded | boolean | false | Whether the FAB is expanded to show additional actions. |