Conditional Hover Effects in shadcn Badge Component
Background
Our project manager noticed some badges had hover effects, making them look like buttons, which caused confusion. We needed to remove hover effects for non-button badges while keeping them for actual buttons. We use the shadcn badge component.
Initial Setup
Here’s the default setup using class-variance-authority (CVA):
import { cva, type VariantProps } from "class-variance-authority";
import cn from "@/utils/cn";
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
);
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
}
export { Badge, badgeVariants };Solution
I conditionally apply hover effects based on the role attribute:
const hoverVariants = cva("", {
variants: {
variant: {
default: "hover:bg-primary/80",
secondary: "hover:bg-secondary/80",
destructive: "hover:bg-destructive/80",
outline: "",
},
},
defaultVariants: {
variant: "default",
},
});
function Badge({ className, variant, role, ...props }: BadgeProps) {
const isRoleButton = role === "button";
return (
<div
className={cn(
badgeVariants({ variant }),
isRoleButton && hoverVariants({ variant }),
className
)}
role={role}
{...props}
/>
);
}Explanation
1.Separated Styles:
badgeVariants: Base styles.hoverVariants: Hover styles.
2.Conditional Application:
- Check if
roleis "button". - Apply
hoverVariantsonly if true.
Usage Example
import React from 'react';
import { Badge } from './path/to/Badge';
const Example = () => (
<div>
<Badge role="button" variant="default">Default Button Badge</Badge>
<Badge role="button" variant="secondary">Secondary Button Badge</Badge>
<Badge variant="destructive">Non-Button Destructive Badge</Badge>
<Badge variant="outline">Non-Button Outline Badge</Badge>
</div>
);
export default Example;Conclusion
This method ensures flexibility by conditionally applying hover effects based on the role attribute. It keeps the component logic clean and maintainable.
Get Dobinshin’s stories in your inbox
Join Medium for free to get updates from this writer.
If you have a better idea, feel free to leave a comment below.









