The @packages/ui package provides a comprehensive collection of reusable React components for Answer Overflow, built with Radix UI primitives and styled with Tailwind CSS.
Installation
Overview
This package includes:
- 50+ UI components (buttons, dialogs, forms, etc.)
- AI-specific components for chat interfaces
- Discord components for rendering Discord content
- Radix UI primitives for accessibility
- Tailwind CSS for styling
- Dark mode support with next-themes
- Analytics integration with PostHog
Core Components
import { Button } from "@packages/ui/components/button";
function MyComponent() {
return (
<>
<Button variant="default">Default</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button disabled>Disabled</Button>
</>
);
}
Button style variant
default - Primary button
destructive - Dangerous actions
outline - Secondary button
ghost - Minimal button
link - Link-style button
Button size
sm - Small
default - Medium
lg - Large
icon - Icon-only
Dialog
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@packages/ui/components/dialog";
function MyDialog() {
return (
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>
Dialog description goes here.
</DialogDescription>
</DialogHeader>
<div>Dialog content</div>
</DialogContent>
</Dialog>
);
}
Card
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@packages/ui/components/card";
function MyCard() {
return (
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card description</CardDescription>
</CardHeader>
<CardContent>
<p>Card content goes here</p>
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
);
}
import { Input } from "@packages/ui/components/input";
import { Label } from "@packages/ui/components/label";
import { useForm } from "react-hook-form";
function MyForm() {
const { register, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<div>
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@example.com"
{...register("email")}
/>
</div>
<Button type="submit">Submit</Button>
</form>
);
}
Select
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@packages/ui/components/select";
function MySelect() {
return (
<Select>
<SelectTrigger>
<SelectValue placeholder="Select an option" />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
<SelectItem value="option3">Option 3</SelectItem>
</SelectContent>
</Select>
);
}
AI Components
Specialized components for AI chat interfaces:
AI Chat Message
import { AIChatMessage } from "@packages/ui/components/ai-elements/chat-message";
function ChatInterface() {
return (
<div>
<AIChatMessage
role="user"
content="Hello, how can you help me?"
/>
<AIChatMessage
role="assistant"
content="I can help you with various tasks!"
streaming={false}
/>
</div>
);
}
Message role: user, assistant, or system
Message content (supports markdown)
Whether the message is currently being streamed
AI Typing Indicator
import { TypingIndicator } from "@packages/ui/components/ai-elements/typing-indicator";
function Chat() {
const [isTyping, setIsTyping] = useState(true);
return (
<div>
{messages.map(msg => <AIChatMessage {...msg} />)}
{isTyping && <TypingIndicator />}
</div>
);
}
Discord Components
Discord Message
import { DiscordMessage } from "@packages/ui/components/discord-message";
function Messages() {
return (
<DiscordMessage
author={{
username: "User123",
avatar: "https://cdn.discordapp.com/...",
}}
content="Hello from Discord!"
timestamp={new Date()}
embeds={[]}
/>
);
}
Discord Embed
import { DiscordEmbed } from "@packages/ui/components/discord-embed";
function MyEmbed() {
return (
<DiscordEmbed
title="Embed Title"
description="Embed description"
color={0x5865F2}
fields={[
{ name: "Field 1", value: "Value 1", inline: true },
{ name: "Field 2", value: "Value 2", inline: true },
]}
footer={{ text: "Footer text" }}
timestamp={new Date()}
/>
);
}
Code Display
import { Code } from "@packages/ui/components/code";
function CodeExample() {
return (
<Code
code={`function hello() {\n console.log("Hello!");\n}`}
language="typescript"
showLineNumbers
highlightLines={[2]}
/>
);
}
Language for syntax highlighting (uses Shiki)
Utilities
Class Name Utilities
import { cn } from "@packages/ui/lib/utils";
function MyComponent({ className }: { className?: string }) {
return (
<div className={cn("base-class", "another-class", className)}>
Content
</div>
);
}
Hooks
import { useToast } from "@packages/ui/hooks/use-toast";
import { useMediaQuery } from "@packages/ui/hooks/use-media-query";
import { useScrollContainer } from "@packages/ui/hooks/use-scroll-container";
function MyComponent() {
const { toast } = useToast();
const isMobile = useMediaQuery("(max-width: 768px)");
const { scrollRef, isAtBottom } = useScrollContainer();
return (
<div ref={scrollRef}>
<Button onClick={() => toast({ title: "Success!" })}>
Show Toast
</Button>
</div>
);
}
Analytics
import { trackEvent } from "@packages/ui/analytics";
import { usePostHog } from "@packages/ui/analytics/client";
function MyComponent() {
const posthog = usePostHog();
const handleClick = () => {
trackEvent("button_clicked", {
component: "MyComponent",
});
posthog?.capture("custom_event", {
property: "value",
});
};
return <Button onClick={handleClick}>Track Me</Button>;
}
Theme
import { ThemeProvider } from "next-themes";
import { useTheme } from "next-themes";
function App({ children }) {
return (
<ThemeProvider attribute="class" defaultTheme="system">
{children}
</ThemeProvider>
);
}
function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<Button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
Toggle Theme
</Button>
);
}
Available Components
- Accordion
- Alert / Alert Dialog
- Avatar
- Badge
- Button / Button Group
- Calendar
- Card
- Carousel
- Chart (Recharts)
- Checkbox
- Collapsible
- Command
- Context Menu
- Dialog
- Dropdown Menu
- Form
- Input / OTP Input
- Label
- Menubar
- Navigation Menu
- Popover
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Slider
- Switch
- Table
- Tabs
- Toast / Sonner
- Toggle / Toggle Group
- Tooltip
Exports
./components/ai-elements/*
AI-specific components
Dependencies
- Radix UI - Accessible component primitives
- Tailwind CSS - Utility-first CSS
- class-variance-authority - Component variants
- lucide-react - Icon library
- next-themes - Theme management
- react-hook-form - Form handling
- recharts - Charts
- sonner - Toast notifications