Overview
The Auth Dashboard includes a library of reusable UI components built with React, TypeScript, and Tailwind CSS. These components provide consistent styling, behavior, and accessibility across the application.
All shared components are located in src/shared/components/ and are designed for composition and reusability.
Component Architecture
The component library follows these principles:
Composition Components are composable and accept children for flexible layouts.
Type Safety Full TypeScript support with proper prop interfaces.
Dark Mode All components support dark mode with Tailwind CSS classes.
Accessibility Built with accessibility in mind using semantic HTML.
Available Components
src/shared/components/
├── Modal.tsx # Modal dialog component
├── PageContainer.tsx # Page wrapper with header
├── Sidebar.tsx # Navigation sidebar
├── Topbar.tsx # Top navigation bar
└── TableSkeleton.tsx # Loading skeleton for tables
Core Components
Modal
A modal dialog component for overlays and confirmations (src/shared/components/Modal.tsx:9-26):
import type { ReactNode } from "react" ;
interface ModalProps {
isOpen : boolean ;
onClose : () => void ;
children : ReactNode ;
}
export const Modal = ({ isOpen , onClose , children } : ModalProps ) => {
if ( ! isOpen ) return null ;
return (
< div className = "fixed inset-0 bg-black/40 flex items-center justify-center z-50" >
< div className = "bg-white dark:bg-slate-900 dark:text-gray-200 rounded-xl shadow-lg w-full max-w-md p-6 relative" >
< button
onClick = { onClose }
className = "absolute right-3 top-3 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300"
>
✕
</ button >
< div className = "w-full" > { children } </ div >
</ div >
</ div >
);
};
Props:
isOpen - Controls modal visibility
onClose - Callback when close button is clicked
children - Modal content
Features:
✅ Dark mode support
✅ Backdrop with opacity
✅ Close button
✅ Centered positioning
✅ Conditional rendering
PageContainer
A wrapper component for consistent page layouts (src/shared/components/PageContainer.tsx:10-36):
import type { ReactNode } from "react" ;
interface PageContainerProps {
title ?: string ;
subtitle ?: string ;
children : ReactNode ;
actions ?: ReactNode ;
}
export const PageContainer = ({
title ,
subtitle ,
children ,
actions ,
} : PageContainerProps ) => {
return (
< div className = "bg-white dark:bg-slate-900 rounded-xl p-6 shadow-sm" >
{ ( title || actions ) && (
< div className = "flex items-center justify-between mb-6" >
< div >
{ title && (
< h1 className = "text-gray-800 dark:text-gray-100" > { title } </ h1 >
) }
{ subtitle && (
< p className = "text-gray-500 dark:text-gray-400" > { subtitle } </ p >
) }
</ div >
{ actions && < div > { actions } </ div > }
</ div >
) }
{ children }
</ div >
);
};
Props:
title - Page heading (optional)
subtitle - Page description (optional)
children - Page content
actions - Action buttons (optional)
Features:
✅ Consistent spacing and styling
✅ Flexible header with optional actions
✅ Dark mode support
✅ Responsive layout
Navigation sidebar with active route highlighting (src/shared/components/Sidebar.tsx:4-52):
import { NavLink } from "react-router-dom" ;
import { useTranslation } from "react-i18next" ;
const SideBar = () => {
const { t } = useTranslation ();
return (
< aside className = "w-64 bg-slate-900 text-white min-h-screen p-4" >
< nav className = "flex flex-col gap-2" >
< NavLink
to = "/"
end
className = { ({ isActive }) =>
`px-3 py-2 rounded transition-colors ${
isActive
? "bg-slate-700 text-white"
: "text-slate-300 hover:bg-slate-800 hover:text-white"
} `
}
>
{ t ( "dashboard" ) }
</ NavLink >
< NavLink
to = "users"
className = { ({ isActive }) =>
`px-3 py-2 rounded transition-colors ${
isActive
? "bg-slate-700 text-white"
: "text-slate-300 hover:bg-slate-800 hover:text-white"
} `
}
>
{ t ( "users" ) }
</ NavLink >
< NavLink
to = "settings"
className = { ({ isActive }) =>
`px-3 py-2 rounded transition-colors ${
isActive
? "bg-slate-700 text-white"
: "text-slate-300 hover:bg-slate-800 hover:text-white"
} `
}
>
{ t ( "settings" ) }
</ NavLink >
</ nav >
</ aside >
);
};
export default SideBar ;
Features:
✅ Active route styling with NavLink
✅ Internationalization support
✅ Hover states
✅ Fixed width layout
DashboardLayout
Main layout component that combines sidebar, topbar, and content area (src/shared/layout/DashboardLayout.tsx:8-27):
import { Outlet } from "react-router-dom" ;
import Topbar from "../components/Topbar" ;
import SideBar from "../components/Sidebar" ;
import { Toast } from "../store/Toast" ;
import { useSettingsStore } from "../store/useSettingsStore" ;
import { useEffect } from "react" ;
export const DashboardLayout = () => {
const theme = useSettingsStore (( s ) => s . theme );
useEffect (() => {
document . documentElement . classList . toggle ( "dark" , theme === "dark" );
}, [ theme ]);
return (
< div className = "flex h-screen overflow-hidden w-full" >
< SideBar />
< div className = "flex-1 flex flex-col bg-slate-100 dark:bg-slate-400" >
< Topbar />
< Toast />
< main className = "flex-1 overflow-y-auto p-6 w-full" >
< Outlet />
</ main >
</ div >
</ div >
);
};
Features:
✅ Sidebar + main content layout
✅ Topbar with user info
✅ Toast notifications
✅ Theme management
✅ Responsive scrolling
Component Patterns
Composition Pattern
Build complex UIs by composing simple components:
function UserDetailsPage () {
const [ showDeleteModal , setShowDeleteModal ] = useState ( false );
return (
< PageContainer
title = "User Details"
actions = {
< button onClick = { () => setShowDeleteModal ( true ) } >
Delete User
</ button >
}
>
< UserProfile />
< UserActivity />
< Modal isOpen = { showDeleteModal } onClose = { () => setShowDeleteModal ( false ) } >
< ConfirmDelete onConfirm = { handleDelete } />
</ Modal >
</ PageContainer >
);
}
Render Props Pattern
For flexible component behavior:
interface DataTableProps < T > {
data : T [];
loading : boolean ;
renderRow : ( item : T ) => ReactNode ;
}
function DataTable < T >({ data , loading , renderRow } : DataTableProps < T >) {
if ( loading ) return < TableSkeleton /> ;
return (
< table >
< tbody >
{ data . map (( item , index ) => (
< tr key = { index } > { renderRow ( item ) } </ tr >
)) }
</ tbody >
</ table >
);
}
// Usage
< DataTable
data = { users }
loading = { loading }
renderRow = { ( user ) => (
<>
< td > { user . name } </ td >
< td > { user . email } </ td >
</>
) }
/> ;
Compound Components Pattern
For complex component APIs:
function Card ({ children } : { children : ReactNode }) {
return (
< div className = "bg-white dark:bg-slate-900 rounded-lg shadow" >
{ children }
</ div >
);
}
Card . Header = function CardHeader ({ children } : { children : ReactNode }) {
return < div className = "p-4 border-b" > { children } </ div > ;
};
Card . Body = function CardBody ({ children } : { children : ReactNode }) {
return < div className = "p-4" > { children } </ div > ;
};
Card . Footer = function CardFooter ({ children } : { children : ReactNode }) {
return < div className = "p-4 border-t" > { children } </ div > ;
};
// Usage
< Card >
< Card.Header >
< h2 > User Profile </ h2 >
</ Card.Header >
< Card.Body >
< UserInfo />
</ Card.Body >
< Card.Footer >
< button > Edit </ button >
</ Card.Footer >
</ Card > ;
Styling Patterns
Tailwind CSS
All components use Tailwind CSS for styling:
function Button ({ variant = "primary" , children , ... props }) {
const baseClasses = "px-4 py-2 rounded-lg font-medium transition-colors" ;
const variantClasses = {
primary: "bg-blue-600 text-white hover:bg-blue-700" ,
secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300" ,
danger: "bg-red-600 text-white hover:bg-red-700" ,
};
return (
< button
className = { ` ${ baseClasses } ${ variantClasses [ variant ] } ` }
{ ... props }
>
{ children }
</ button >
);
}
Dark Mode
Use dark: prefix for dark mode styles:
< div className = "bg-white dark:bg-slate-900 text-gray-900 dark:text-gray-100" >
< p className = "text-gray-600 dark:text-gray-400" > Description text </ p >
</ div >
Conditional Classes
Use template literals for dynamic classes:
function Badge ({ status } : { status : "active" | "inactive" }) {
return (
< span
className = { `px-2 py-1 rounded ${
status === "active"
? "bg-green-100 text-green-800"
: "bg-gray-100 text-gray-800"
} ` }
>
{ status }
</ span >
);
}
Creating New Components
Create component file
Add a new file in src/shared/components/: // src/shared/components/Alert.tsx
import type { ReactNode } from "react" ;
interface AlertProps {
type : "info" | "success" | "warning" | "error" ;
children : ReactNode ;
}
export function Alert ({ type , children } : AlertProps ) {
const styles = {
info: "bg-blue-100 text-blue-800 border-blue-200" ,
success: "bg-green-100 text-green-800 border-green-200" ,
warning: "bg-yellow-100 text-yellow-800 border-yellow-200" ,
error: "bg-red-100 text-red-800 border-red-200" ,
};
return (
< div className = { `p-4 border rounded-lg ${ styles [ type ] } ` } >
{ children }
</ div >
);
}
Add TypeScript types
Define clear prop interfaces: interface AlertProps {
type : "info" | "success" | "warning" | "error" ;
children : ReactNode ;
onClose ?: () => void ;
}
Support dark mode
Add dark mode variants: const styles = {
info: "bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200" ,
// ... other variants
};
Export and use
Import and use in your pages: import { Alert } from "@/shared/components/Alert" ;
< Alert type = "success" >
User created successfully!
</ Alert >
Best Practices
Small & Focused Keep components small and focused on a single responsibility.
Prop Interfaces Always define TypeScript interfaces for props.
Composition Prefer composition over complex prop APIs.
Accessibility Use semantic HTML and ARIA attributes.
Component Checklist
Must Have
Should Have
Nice to Have
✅ TypeScript prop interface
✅ Dark mode support
✅ Semantic HTML elements
✅ Clear, descriptive prop names
✅ Handle edge cases (empty states, errors)
✅ Support for children prop
✅ Optional className prop for overrides
✅ Loading states
✅ Error states
✅ Accessibility attributes
✅ Animation/transitions
✅ Keyboard navigation
✅ Mobile responsive
✅ Unit tests
✅ Storybook stories
Common Pitfalls
Avoid these common mistakes when building components:
Problem: Passing props through many levels// ❌ Prop drilling
< Layout user = { user } >
< Sidebar user = { user } >
< UserMenu user = { user } />
</ Sidebar >
</ Layout >
Solution: Use context or global state// ✅ Use auth store
function UserMenu () {
const user = useAuthStore (( state ) => state . user );
return < div > { user ?. name } </ div > ;
}
Problem: Not using keys in lists// ❌ No key
{ users . map ( user => < UserCard user = { user } /> )}
Solution: Always provide unique keys// ✅ With key
{ users . map ( user => < UserCard key = { user . id } user = { user } /> )}
Inline Function Definitions
Problem: Creating new functions on every render// ❌ New function each render
< button onClick = { () => handleClick ( item . id ) } > Click </ button >
Solution: Use useCallback or move outside render// ✅ Memoized function
const handleClick = useCallback (() => {
handleItemClick ( item . id );
}, [ item . id ]);
< button onClick = { handleClick } > Click </ button >
Next Steps
Architecture Learn where components fit in the architecture
State Management Use stores in components
Tailwind CSS Official Tailwind CSS documentation
React Docs Official React documentation