๐Ÿ””Welcome

HaloLight multi-framework admin dashboard docs is now live!

Supports 12+ framework versions. Welcome to try.

Skip to content

React Version โ€‹

HaloLight React version is built on React 19 + Vite 6, a pure Client-Side Rendering (CSR) Single Page Application (SPA).

Live Preview: https://halolight-react.h7ml.cn/

GitHub: https://github.com/halolight/halolight-react

Tech Stack โ€‹

TechnologyVersionDescription
React19.xUI framework
Vite6.xBuild tool
TypeScript5.xType safety
React Router6.xClient-side routing
Zustand5.xState management
TanStack Query5.xServer state
React Hook Form7.xForm handling
Zod4.xData validation
Tailwind CSS4.xAtomic CSS
shadcn/uilatestUI component library
react-grid-layout1.5.xDrag-and-drop layout
Recharts3.xChart visualization
Framer Motion12.xAnimation effects
Mock.js1.xData mocking

Directory Structure โ€‹

halolight-react/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ pages/                     # Page components
โ”‚   โ”‚   โ”œโ”€โ”€ auth/                  # Auth pages
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ login/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ register/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ forgot-password/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ reset-password/
โ”‚   โ”‚   โ”œโ”€โ”€ dashboard/             # Dashboard
โ”‚   โ”‚   โ””โ”€โ”€ legal/                 # Legal pages
โ”‚   โ”œโ”€โ”€ components/
โ”‚   โ”‚   โ”œโ”€โ”€ ui/                    # shadcn/ui components (20+)
โ”‚   โ”‚   โ”œโ”€โ”€ layout/                # Layout components
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ admin-layout.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ auth-layout.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ sidebar.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ header.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ footer.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ dashboard/             # Dashboard components
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ configurable-dashboard.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ widget-wrapper.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ stats-widget.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ chart-widget.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”‚   โ””โ”€โ”€ shared/                # Shared components
โ”‚   โ”œโ”€โ”€ hooks/                     # Custom Hooks
โ”‚   โ”‚   โ”œโ”€โ”€ use-users.ts
โ”‚   โ”‚   โ”œโ”€โ”€ use-auth.ts
โ”‚   โ”‚   โ”œโ”€โ”€ use-theme.ts
โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”œโ”€โ”€ stores/                    # Zustand Stores
โ”‚   โ”‚   โ”œโ”€โ”€ auth.ts
โ”‚   โ”‚   โ”œโ”€โ”€ ui-settings.ts
โ”‚   โ”‚   โ”œโ”€โ”€ dashboard-layout.ts
โ”‚   โ”‚   โ””โ”€โ”€ tabs.ts
โ”‚   โ”œโ”€โ”€ lib/
โ”‚   โ”‚   โ”œโ”€โ”€ api/                   # API services
โ”‚   โ”‚   โ”œโ”€โ”€ auth/                  # Auth logic
โ”‚   โ”‚   โ”œโ”€โ”€ validations/           # Zod schemas
โ”‚   โ”‚   โ””โ”€โ”€ utils.ts               # Utility functions
โ”‚   โ”œโ”€โ”€ routes/                    # Route configuration
โ”‚   โ”‚   โ””โ”€โ”€ index.tsx
โ”‚   โ”œโ”€โ”€ config/                    # Configuration files
โ”‚   โ”‚   โ”œโ”€โ”€ routes.ts
โ”‚   โ”‚   โ””โ”€โ”€ tdk.ts
โ”‚   โ”œโ”€โ”€ types/                     # Type definitions
โ”‚   โ”œโ”€โ”€ mock/                      # Mock data
โ”‚   โ”œโ”€โ”€ providers/                 # Context Providers
โ”‚   โ”œโ”€โ”€ App.tsx
โ”‚   โ””โ”€โ”€ main.tsx
โ”œโ”€โ”€ public/                        # Static assets
โ”œโ”€โ”€ vite.config.ts
โ”œโ”€โ”€ tsconfig.json
โ””โ”€โ”€ package.json

Quick Start โ€‹

Installation โ€‹

bash
git clone https://github.com/halolight/halolight-react.git
cd halolight-react
pnpm install

Environment Variables โ€‹

bash
cp .env.example .env.development
env
# .env.development example
VITE_API_URL=/api
VITE_MOCK=true
VITE_APP_TITLE=Admin Pro
VITE_BRAND_NAME=Halolight
VITE_DEMO_EMAIL=admin@halolight.h7ml.cn
VITE_DEMO_PASSWORD=123456
VITE_SHOW_DEMO_HINT=true

Start Development โ€‹

bash
pnpm dev

Visit http://localhost:5173

Build for Production โ€‹

bash
pnpm build
pnpm preview

Core Features โ€‹

State Management (Zustand) โ€‹

tsx
// stores/auth.ts
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface AuthState {
  user: User | null
  token: string | null
  isAuthenticated: boolean
  login: (credentials: LoginCredentials) => Promise<void>
  logout: () => void
  hasPermission: (permission: string) => boolean
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set, get) => ({
      user: null,
      token: null,
      isAuthenticated: false,

      login: async (credentials) => {
        const response = await authApi.login(credentials)
        set({
          user: response.user,
          token: response.token,
          isAuthenticated: true,
        })
      },

      logout: () => {
        set({ user: null, token: null, isAuthenticated: false })
      },

      hasPermission: (permission) => {
        const { user } = get()
        if (!user) return false
        return user.permissions.some(p =>
          p === '*' || p === permission ||
          (p.endsWith(':*') && permission.startsWith(p.slice(0, -1)))
        )
      },
    }),
    {
      name: 'auth-storage',
      partialize: (state) => ({ token: state.token, user: state.user }),
    }
  )
)

Data Fetching (TanStack Query) โ€‹

tsx
// hooks/use-users.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { usersApi } from '@/lib/api'

export function useUsers(params?: UserQueryParams) {
  return useQuery({
    queryKey: ['users', params],
    queryFn: () => usersApi.getList(params),
  })
}

export function useCreateUser() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: usersApi.create,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] })
    },
  })
}

// Usage in component
function UsersPage() {
  const { data: users, isLoading, error } = useUsers()

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return <div>{/* Render user list */}</div>
}

Route Configuration (React Router) โ€‹

tsx
// routes/index.tsx
import { createBrowserRouter, Navigate } from 'react-router-dom'
import { DashboardLayout } from '@/layouts/dashboard-layout'
import { AuthLayout } from '@/layouts/auth-layout'

export const router = createBrowserRouter([
  {
    path: '/',
    element: <Navigate to="/dashboard" replace />,
  },
  {
    path: '/login',
    element: <AuthLayout />,
    children: [
      { index: true, element: <LoginPage /> },
    ],
  },
  {
    path: '/',
    element: <DashboardLayout />,
    children: [
      { path: 'dashboard', element: <HomePage /> },
      { path: 'users', element: <UsersPage /> },
      { path: 'settings', element: <SettingsPage /> },
      // More routes...
    ],
  },
])

Permission Hook โ€‹

tsx
// hooks/use-permission.ts
import { useAuthStore } from '@/stores/auth'

export function usePermission(permission: string): boolean {
  const hasPermission = useAuthStore((state) => state.hasPermission)
  return hasPermission(permission)
}

export function usePermissions(permissions: string[]): boolean {
  const hasPermission = useAuthStore((state) => state.hasPermission)
  return permissions.every(p => hasPermission(p))
}
tsx
// Usage
function DeleteButton() {
  const canDelete = usePermission('users:delete')

  if (!canDelete) return null

  return <Button variant="destructive">Delete</Button>
}

Permission Component โ€‹

tsx
// components/permission-guard.tsx
import { usePermission } from '@/hooks/use-permission'

interface PermissionGuardProps {
  permission: string
  children: React.ReactNode
  fallback?: React.ReactNode
}

export function PermissionGuard({
  permission,
  children,
  fallback = null,
}: PermissionGuardProps) {
  const hasPermission = usePermission(permission)

  if (!hasPermission) return fallback

  return <>{children}</>
}
tsx
<!-- Usage -->
<PermissionGuard permission="users:delete" fallback={<span>No permission</span>}>
  <DeleteButton />
</PermissionGuard>

Draggable Dashboard โ€‹

tsx
// components/dashboard/configurable-dashboard.tsx
import GridLayout from 'react-grid-layout'
import { useDashboardStore } from '@/stores/dashboard-layout'

export function ConfigurableDashboard() {
  const { layout, setLayout, isEditing } = useDashboardStore()

  return (
    <GridLayout
      layout={layout}
      onLayoutChange={setLayout}
      cols={12}
      rowHeight={80}
      isDraggable={isEditing}
      isResizable={isEditing}
      margin={[16, 16]}
    >
      {layout.map((item) => (
        <div key={item.i}>
          <WidgetWrapper widget={getWidget(item.i)} />
        </div>
      ))}
    </GridLayout>
  )
}

Theme Toggle โ€‹

tsx
// hooks/use-theme.ts
import { useEffect, useState } from 'react'

type Theme = 'light' | 'dark' | 'system'

export function useTheme() {
  const [theme, setTheme] = useState<Theme>('system')

  const actualTheme = theme === 'system'
    ? window.matchMedia('(prefers-color-scheme: dark)').matches
      ? 'dark'
      : 'light'
    : theme

  useEffect(() => {
    document.documentElement.classList.remove('light', 'dark')
    document.documentElement.classList.add(actualTheme)
  }, [actualTheme])

  const toggleTheme = () => {
    setTheme(actualTheme === 'dark' ? 'light' : 'dark')
  }

  return { theme, actualTheme, setTheme, toggleTheme }
}

Page Routes โ€‹

PathPagePermission
/Redirect to /dashboard-
/loginLoginPublic
/registerRegisterPublic
/forgot-passwordForgot passwordPublic
/reset-passwordReset passwordPublic
/dashboardDashboarddashboard:view
/usersUser listusers:list
/users/createCreate userusers:create
/users/:idUser detailsusers:view
/users/:id/editEdit userusers:update
/rolesRole managementroles:list
/permissionsPermission managementpermissions:list
/settingsSystem settingssettings:view
/profileUser profileAuthenticated

Route Guards โ€‹

tsx
// components/auth-guard.tsx
import { Navigate, useLocation } from 'react-router-dom'
import { useAuthStore } from '@/stores/auth'

interface AuthGuardProps {
  children: React.ReactNode
  permission?: string
}

export function AuthGuard({ children, permission }: AuthGuardProps) {
  const location = useLocation()
  const { isAuthenticated, hasPermission } = useAuthStore()

  if (!isAuthenticated) {
    return <Navigate to="/login" state={{ from: location }} replace />
  }

  if (permission && !hasPermission(permission)) {
    return <Navigate to="/403" replace />
  }

  return <>{children}</>
}

UI Components โ€‹

Based on shadcn/ui, 20+ components integrated:

  • Forms: Button, Input, Textarea, Select, Checkbox, RadioGroup, Switch, Slider, DatePicker
  • Data Display: Table, Card, Badge, Avatar, Progress, Skeleton
  • Feedback: Dialog, Sheet, AlertDialog, Toast, Tooltip, Popover
  • Navigation: Tabs, Breadcrumb, Pagination, DropdownMenu, Command
  • Layout: Accordion, Collapsible, ScrollArea, Separator

Recharts Integration โ€‹

tsx
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'

const data = [
  { name: 'Jan', value: 400 },
  { name: 'Feb', value: 300 },
  { name: 'Mar', value: 600 },
  // ...
]

function Chart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <LineChart data={data}>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="name" />
        <YAxis />
        <Tooltip />
        <Line type="monotone" dataKey="value" stroke="#8884d8" />
      </LineChart>
    </ResponsiveContainer>
  )
}

PWA Support โ€‹

Built-in PWA support including:

  • Service Worker registration
  • Offline caching
  • App manifest (manifest.json)
  • Multiple icon sizes
json
// public/manifest.json
{
  "name": "Admin Pro",
  "short_name": "Admin",
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "icons": [
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    }
  ]
}

Deployment โ€‹

Vercel โ€‹

bash
vercel

Netlify โ€‹

bash
netlify deploy --prod

Nginx โ€‹

nginx
server {
    listen 80;
    server_name example.com;
    root /var/www/halolight-react/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_pass http://backend:3000;
    }
}

Docker โ€‹

dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Comparison with Next.js Version โ€‹

FeatureReact VersionNext.js Version
RenderingCSR (Client-Side)SSR/SSG/ISR
State ManagementZustandZustand
Data FetchingTanStack QueryTanStack Query
Form ValidationReact Hook Form + ZodReact Hook Form + Zod
Drag-and-Dropreact-grid-layoutreact-grid-layout
UI Componentsshadcn/uishadcn/ui
RoutingReact RouterNext.js App Router
ChartsRechartsRecharts
SSRNoneBuilt-in
SEOreact-helmet-asyncBuilt-in
DeploymentStatic hosting / CDNVercel / Any platform

When to Choose React Version? โ€‹

  • Pure frontend app with separate backend API
  • No SSR/SEO optimization needed
  • Already have a backend service, just need frontend admin interface
  • Want a lighter tech stack
  • Deploying to pure static hosting services

When to Choose Next.js Version? โ€‹

  • Need SEO optimization
  • Need SSR/SSG for better initial load performance
  • Want frontend-backend integration
  • Need API Routes / Server Actions
  • Deploying to full-stack platforms like Vercel