My Shadcn/ui powered Components
- Created: September 17, 2025 10:43 AM
- Categories: Snippets
- Status:Growing
Header navigation
Requirements
bashpnpm dlx shadcn@latest add button drawer separator
Components
tsx"use client";
import type { ReactNode } from "react";
import { usePathname } from "next/navigation";
import { cn } from "@/lib/utils";
import Link from "next/link";
interface NavLinkProps {
href: string;
className?: string;
children: React.ReactNode;
mobile?: boolean;
}
export function NavLink({
href,
className,
children,
mobile,
...props
}: NavLinkProps): ReactNode | null {
const pathname = usePathname();
const isActive =
href === "/"
? pathname === "/"
: pathname === href || pathname.startsWith(`${href}/`);
return (
<Link
className={cn(
"hover:text-primary text-base capitalize transition-colors",
isActive ? "text-primary font-bold" : "text-foreground",
isActive && mobile && "bg-primary text-white",
mobile && "border p-4",
className,
)}
href={href}
{...props}
>
{children}
</Link>
);
}
```</ToggleList>
<br />
<ToggleList summary={<Color type="boolean">@/components/navigation/main-nav.tsx</Color>}>
```tsx
import { navigation } from "@/config";
import { Button } from "../ui/button";
import { NavLink } from "./nav-link";
import Link from "next/link";
export default function MainNav() {
return (
<nav className="hidden items-center gap-6 text-sm md:flex">
<div className="flex gap-6">
{navigation.map((link) => (
<NavLink key={link.href} href={link.href}>
{link.label}
</NavLink>
))}
</div>
{/* Call to action button */}
<Button asChild className="bg-primary px-6">
<Link href="/join">Call to action!</Link>
</Button>
</nav>
);
}
tsximport { Menu } from "lucide-react";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
import { Button } from "@/components/ui/button";
import { navigation } from "@/config";
import { Logo } from "@/assets/vectors";
import { Separator } from "@/components/ui/separator";
import { NavLink } from "./nav-link";
export default function MobileNav() {
return (
<Drawer>
<DrawerTrigger className="block md:hidden">
<Menu />
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Navigation</DrawerTitle>
<DrawerDescription>Page navigation</DrawerDescription>
</DrawerHeader>
<div className="px-4">
<Separator />
</div>
<nav className="flex max-h-[50vh] w-full flex-col gap-4 overflow-auto p-4">
{navigation.map((link) => (
<NavLink mobile key={link.href} href={link.href}>
{link.label}
</NavLink>
))}
</nav>
<div className="px-4">
<Separator />
</div>
<DrawerFooter>
<NavLink
href="/"
className="text-primary mb-1 flex items-center gap-2 self-center font-semibold"
>
<Logo className="fill-primary" width={36} height={36} />
<span className="text-2xl font-bold">My page</span>
</NavLink>
<DrawerClose asChild>
<Button variant="outline">Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
);
}
tsximport { Logo } from "@/assets/vectors";
import { NavLink } from "./navigation/nav-link";
import MainNav from "./navigation/main-nav";
import MobileNav from "./navigation/mobile-nav";
export default function Header() {
return (
<header className="w-full">
<div className="mx-6 flex items-center py-6 md:mx-10">
<div className="flex w-full items-center justify-between gap-6">
<NavLink
href="/"
className="text-primary flex items-center gap-2 font-semibold"
>
<Logo className="fill-primary" width={36} height={36} />
<span className="text-2xl font-bold">My Page</span>
</NavLink>
<MainNav />
<MobileNav />
</div>
</div>
</header>
);
}
Dialog Provider
Requirements
bashpnpm dlx shadcn@latest add dialog
tsx"use client";
import { createContext, useContext, useState, type ReactNode } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
} from "@/components/ui/dialog";
type DialogOptions = {
title?: string;
description?: string;
content?: () => ReactNode;
};
type DialogContextType = {
openDialog: (options: DialogOptions) => void;
closeDialog: () => void;
};
const DialogContext = createContext<DialogContextType | undefined>(undefined);
export const DialogProvider = ({ children }: { children: ReactNode }) => {
const [isOpen, setIsOpen] = useState(false);
const [dialogContent, setDialogContent] = useState<DialogOptions>({});
const openDialog = (options: DialogOptions) => {
setDialogContent(options);
setIsOpen(true);
};
const closeDialog = () => {
setIsOpen(false);
// Delay unmounting until after dialog transition ends
setTimeout(() => {
setDialogContent({});
}, 200); // Match Dialog exit animation duration
};
return (
<DialogContext.Provider value={{ openDialog, closeDialog }}>
{children}
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className="max-h-[90vh]">
<DialogHeader>
{dialogContent.title && (
<DialogTitle>{dialogContent.title}</DialogTitle>
)}
{dialogContent.description && (
<DialogDescription>{dialogContent.description}</DialogDescription>
)}
</DialogHeader>
{dialogContent.content?.()}
</DialogContent>
</Dialog>
</DialogContext.Provider>
);
};
export const useDialog = (): DialogContextType => {
const context = useContext(DialogContext);
if (!context) {
throw new Error("useDialog must be used within a DialogProvider");
}
return context;
};
Multi-value inputs
tsximport * as React from "react";
import { X } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
interface FeatureInputProps {
value: string[];
onChange: (features: string[]) => void;
placeholder?: string;
}
export function FeatureInput({
value = [],
onChange,
placeholder,
}: FeatureInputProps) {
const [inputValue, setInputValue] = React.useState("");
const inputRef = React.useRef<HTMLInputElement>(null);
const handleAddFeature = () => {
if (inputValue.trim() !== "" && !value.includes(inputValue.trim())) {
onChange([...value, inputValue.trim()]);
setInputValue("");
}
};
const handleRemoveFeature = (featureToRemove: string) => {
onChange(value.filter((feature) => feature !== featureToRemove));
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
e.preventDefault();
handleAddFeature();
}
};
return (
<div>
<div className="mb-4 flex items-center gap-2">
<Input
ref={inputRef}
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={placeholder ?? "Add a feature..."}
/>
<Button type="button" onClick={handleAddFeature}>
Add
</Button>
</div>
<div className="flex flex-wrap gap-2">
{value.map((feature, index) => (
<Badge key={index} variant="secondary">
{feature}
<button
type="button"
className="ring-offset-background focus:ring-ring ml-2 rounded-full outline-none focus:ring-2 focus:ring-offset-2"
onClick={() => handleRemoveFeature(feature)}
>
<X className="text-muted-foreground hover:text-foreground h-3 w-3" />
</button>
</Badge>
))}
</div>
</div>
);
}
Miscellaneous Snippets
Scroll for Chrome
css::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: rgba(100, 100, 100, 0.4);
border-radius: 9999px;
border: 2px solid transparent; /* optional padding */
background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
background-color: rgba(100, 100, 100, 0.6);
}
Loader
tsx<Loader2 className="mr-2 h-6 w-6 animate-spin" />

