Themed Components
The theme system provides a component abstraction layer that allows themes to define their own UI implementations while maintaining a consistent API.
How It Works
Instead of importing components directly, you access them through the theme context:
// Traditional import (theme-agnostic)
import { Button } from '@/components/ui/button';
// Themed import (adapts to active theme)
import { useThemedComponent } from '@/hooks/use-theme';
const Button = useThemedComponent('Button');
When you switch themes, the component implementation changes automatically.
Component Categories
UI Primitives
Basic building blocks provided by every theme.
| Component | Description |
|---|---|
Button |
Action buttons |
Input |
Text inputs |
Label |
Form labels |
Checkbox |
Checkbox inputs |
Switch |
Toggle switches |
Select |
Dropdown selects |
Textarea |
Multi-line inputs |
Slider |
Range sliders |
Calendar |
Date picker |
Cards
Container components for grouping content.
| Component | Description |
|---|---|
Card |
Card container |
CardHeader |
Card header section |
CardTitle |
Card title text |
CardDescription |
Card description text |
CardContent |
Card body content |
CardFooter |
Card footer section |
Feedback
Components for user feedback.
| Component | Description |
|---|---|
Alert |
Alert messages |
AlertDescription |
Alert content |
Badge |
Status badges |
Progress |
Progress bars |
Skeleton |
Loading placeholders |
Dialogs & Overlays
Modal and overlay components.
| Component | Description |
|---|---|
Dialog |
Modal dialogs |
DialogTrigger |
Dialog open trigger |
DialogContent |
Dialog body |
DialogHeader |
Dialog header |
DialogTitle |
Dialog title |
DialogDescription |
Dialog description |
DialogFooter |
Dialog footer |
Sheet |
Slide-out panels |
Popover |
Popover overlays |
Tooltip |
Hover tooltips |
Navigation
Navigation components.
| Component | Description |
|---|---|
Tabs |
Tab container |
TabsList |
Tab button list |
TabsTrigger |
Tab button |
TabsContent |
Tab panel |
DropdownMenu |
Dropdown menus |
Breadcrumb |
Breadcrumb navigation |
Pagination |
Page navigation |
Data Display
Components for displaying data.
| Component | Description |
|---|---|
Table |
Data tables |
TableHeader |
Table header |
TableBody |
Table body |
TableRow |
Table row |
TableHead |
Header cell |
TableCell |
Body cell |
Avatar |
User avatars |
Layout
Layout and structure components.
| Component | Description |
|---|---|
Separator |
Visual dividers |
ScrollArea |
Scrollable areas |
Accordion |
Collapsible sections |
Collapsible |
Collapsible content |
Templates
Full page templates.
| Template | Description |
|---|---|
LoginTemplate |
Login page |
TwoFactorTemplate |
2FA verification |
TenantFinderTemplate |
Tenant discovery |
DashboardTemplate |
Main dashboard |
ModulesPageTemplate |
Modules listing |
SettingsProfileTemplate |
Profile settings |
Layouts
Page layout wrappers.
| Layout | Description |
|---|---|
EnterpriseLayout |
Main app layout with sidebar |
AuthLayout |
Authentication pages layout |
Using Themed Components
Single Component
import { useThemedComponent } from '@/hooks/use-theme';
function MyPage() {
const Button = useThemedComponent('Button');
return (
<Button variant="default" onClick={handleClick}>
Click me
</Button>
);
}
Multiple Components
import { useThemedComponents } from '@/hooks/use-theme';
function MyPage() {
const { Card, CardHeader, CardTitle, CardContent, Button } = useThemedComponents([
'Card',
'CardHeader',
'CardTitle',
'CardContent',
'Button',
]);
return (
<Card>
<CardHeader>
<CardTitle>My Card</CardTitle>
</CardHeader>
<CardContent>
<Button>Action</Button>
</CardContent>
</Card>
);
}
Using ThemedLayout
For pages, use the ThemedLayout component which automatically uses the themed layout:
import { ThemedLayout } from '@/components/themed-layout';
export default function MyPage() {
return (
<ThemedLayout>
<div className="p-6">
{/* Page content */}
</div>
</ThemedLayout>
);
}
Component Abstraction
Abstract Interface
Components follow abstract interfaces to ensure compatibility:
// Abstract button interface
interface AbstractButtonProps {
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
size?: 'default' | 'sm' | 'lg' | 'icon';
disabled?: boolean;
loading?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
// Both themes implement this interface
// ThemeShadcn: uses shadcn/ui Button
// ThemeModern: uses custom gradient Button
Theme Implementation
Each theme provides its own implementation:
// ThemeShadcn/frontend/index.tsx
import { Button } from '@/components/ui/button';
const components = {
Button: Button,
// ...
};
// ThemeModern/frontend/index.tsx
import { Button } from './components/ui/button'; // Custom implementation
const components = {
Button: Button, // Different visual style
// ...
};
Creating Themed Components
Step 1: Define the Abstract Interface
// frontend/lib/theme/interfaces/primitives.ts
export interface AbstractAlertProps {
variant?: 'default' | 'destructive' | 'warning' | 'success';
title?: string;
children: React.ReactNode;
className?: string;
}
Step 2: Add to ThemeComponents Type
// frontend/lib/theme/types.ts
export interface ThemeComponents {
// Existing...
MyNewComponent: React.ComponentType<AbstractMyNewComponentProps>;
}
Step 3: Implement in Each Theme
// modules/ThemeShadcn/frontend/components/ui/my-component.tsx
export function MyNewComponent({ variant, children }: AbstractMyNewComponentProps) {
return <div className={cn('...', variantStyles[variant])}>{children}</div>;
}
// modules/ThemeShadcn/frontend/index.tsx
const components = {
// ...
MyNewComponent: MyNewComponent,
};
Step 4: Register in Theme Config
export const themeConfig: ThemeConfig = {
// ...
components: {
// ...
MyNewComponent: MyNewComponent,
},
};
Templates vs Components
Components
Small, reusable UI pieces:
const Button = useThemedComponent('Button');
const Card = useThemedComponent('Card');
Templates
Full page layouts that compose multiple components:
// A template is a complete page design
const DashboardTemplate = useThemedComponent('DashboardTemplate');
// Usage in page
<DashboardTemplate
stats={statsData}
recentOrders={ordersData}
user={userData}
/>
Templates typically:
- Compose many primitive components
- Define page structure and layout
- Handle page-level state
- Are more opinionated about design
Fallback Components
When a theme doesn't provide a component, the system:
- Checks for fallback components
- Falls back to a "missing component" placeholder
- Logs a warning in development
// ThemeProvider with fallbacks
<ThemeProvider
fallbacks={{
MyNewComponent: DefaultMyNewComponent,
}}
>
{children}
</ThemeProvider>
Performance
Memoization
Themed components are memoized to prevent unnecessary re-renders:
const Button = useThemedComponent('Button');
// Button reference is stable across renders
Code Splitting
Themes are loaded dynamically:
// Theme loader (lazy loading)
const loader = async () => {
const module = await import('@modules/ThemeModern/frontend/index');
return module.themeConfig;
};
Best Practices
1. Use Themed Components for Consistency
// Good - consistent with theme
const Button = useThemedComponent('Button');
// Avoid - bypasses theme system
import { Button } from '@/components/ui/button';
2. Batch Component Imports
// Good - single hook call
const { Button, Card, Input } = useThemedComponents(['Button', 'Card', 'Input']);
// Avoid - multiple hook calls
const Button = useThemedComponent('Button');
const Card = useThemedComponent('Card');
const Input = useThemedComponent('Input');
3. Use CSS Variables for Custom Styles
// Good - respects theme
<div className="bg-background text-foreground border-border">
// Avoid - hardcoded colors
<div className="bg-white text-gray-900 border-gray-200">
4. Create Templates for Complex Pages
Instead of putting layout logic in pages, create templates:
// Template handles all the styling
<SettingsTemplate
title="Profile"
description="Manage your account"
sections={[...]}
/>
Next Steps
- Creating Themes - Build a complete theme
- Design Tokens - Customize component styling