Skip to main content

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

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

1

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>
  );
}
2

Add TypeScript types

Define clear prop interfaces:
interface AlertProps {
  type: "info" | "success" | "warning" | "error";
  children: ReactNode;
  onClose?: () => void;
}
3

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
};
4

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

  • ✅ TypeScript prop interface
  • ✅ Dark mode support
  • ✅ Semantic HTML elements
  • ✅ Clear, descriptive prop names
  • ✅ Handle edge cases (empty states, errors)

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} />)}
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

Build docs developers (and LLMs) love