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:

  1. Checks for fallback components
  2. Falls back to a "missing component" placeholder
  3. 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