๐Ÿ””Welcome

HaloLight multi-framework admin dashboard docs is now live!

Supports 12+ framework versions. Welcome to try.

Skip to content

Remix Version โ€‹

HaloLight Remix version is built on React Router 7 (the original Remix team has merged into React Router), featuring TypeScript + Web standards-first full-stack development experience.

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

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

Tech Stack โ€‹

TechnologyVersionDescription
React Router7.xFull-stack routing framework (formerly Remix)
React19.xUI framework
TypeScript5.9Type safety
Vite7.xBuild tool
Tailwind CSS4.xAtomic CSS + OKLch
Radix UIlatestAccessible UI primitives
Zustand5.xLightweight state management
Recharts3.xChart visualization
Vitest4.xUnit testing
Cloudflare Pages-Edge deployment

Core Features โ€‹

  • Web Standards: Based on Web Fetch API, FormData, Response
  • Nested Routes: Powerful nested layouts and data loading
  • Progressive Enhancement: Forms work without JS
  • Loader/Action: Elegant server-side data patterns
  • Type Safety: Auto-generated route types
  • Theme System: 11 skin presets + dark mode
  • Multi-tabs: Tab bar + right-click menu management

Directory Structure โ€‹

halolight-remix/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ routes/                    # File-based routing
โ”‚   โ”‚   โ”œโ”€โ”€ _index.tsx            # Homepage (dashboard)
โ”‚   โ”‚   โ”œโ”€โ”€ login.tsx             # Login
โ”‚   โ”‚   โ”œโ”€โ”€ register.tsx          # Register
โ”‚   โ”‚   โ”œโ”€โ”€ forgot-password.tsx   # Forgot password
โ”‚   โ”‚   โ”œโ”€โ”€ reset-password.tsx    # Reset password
โ”‚   โ”‚   โ”œโ”€โ”€ users.tsx             # User management
โ”‚   โ”‚   โ”œโ”€โ”€ settings.tsx          # System settings
โ”‚   โ”‚   โ”œโ”€โ”€ profile.tsx           # Profile
โ”‚   โ”‚   โ”œโ”€โ”€ security.tsx          # Security settings
โ”‚   โ”‚   โ”œโ”€โ”€ analytics.tsx         # Data analytics
โ”‚   โ”‚   โ”œโ”€โ”€ notifications.tsx     # Notification center
โ”‚   โ”‚   โ”œโ”€โ”€ documents.tsx         # Document management
โ”‚   โ”‚   โ”œโ”€โ”€ calendar.tsx          # Calendar
โ”‚   โ”‚   โ””โ”€โ”€ +types/               # Auto-generated types
โ”‚   โ”œโ”€โ”€ components/               # Component library
โ”‚   โ”‚   โ”œโ”€โ”€ ui/                   # Base UI components
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ button.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ card.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dialog.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dropdown-menu.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ input.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ select.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ table.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”‚   โ”œโ”€โ”€ layout/               # Layout components
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ footer.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ tab-bar.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ quick-settings.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ auth/                 # Auth components
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ auth-shell.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ admin-layout.tsx      # Admin layout
โ”‚   โ”‚   โ””โ”€โ”€ theme-provider.tsx    # Theme provider
โ”‚   โ”œโ”€โ”€ hooks/                    # React Hooks
โ”‚   โ”‚   โ””โ”€โ”€ use-chart-palette.ts
โ”‚   โ”œโ”€โ”€ lib/                      # Utilities
โ”‚   โ”‚   โ”œโ”€โ”€ utils.ts              # cn() className utility
โ”‚   โ”‚   โ”œโ”€โ”€ meta.ts               # TDK meta info
โ”‚   โ”‚   โ””โ”€โ”€ project-info.ts       # Project info
โ”‚   โ”œโ”€โ”€ stores/                   # Zustand state
โ”‚   โ”‚   โ”œโ”€โ”€ tabs-store.ts         # Tabs state
โ”‚   โ”‚   โ””โ”€โ”€ ui-settings-store.ts  # UI settings state
โ”‚   โ”œโ”€โ”€ root.tsx                  # Root component
โ”‚   โ”œโ”€โ”€ routes.ts                 # Route config
โ”‚   โ””โ”€โ”€ app.css                   # Global styles
โ”œโ”€โ”€ tests/                        # Test files
โ”‚   โ”œโ”€โ”€ setup.ts
โ”‚   โ”œโ”€โ”€ lib/
โ”‚   โ”œโ”€โ”€ stores/
โ”‚   โ””โ”€โ”€ components/
โ”œโ”€โ”€ .github/workflows/ci.yml      # CI config
โ”œโ”€โ”€ wrangler.json                 # Cloudflare config
โ”œโ”€โ”€ vitest.config.ts              # Vitest config
โ”œโ”€โ”€ eslint.config.js              # ESLint config
โ””โ”€โ”€ package.json

Quick Start โ€‹

Installation โ€‹

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

Start Development โ€‹

bash
pnpm dev

Visit http://localhost:5173

Production Build โ€‹

bash
pnpm build
pnpm start

Available Commands โ€‹

bash
pnpm dev          # Start dev server
pnpm build        # Production build
pnpm start        # Start production server
pnpm typecheck    # Type checking
pnpm lint         # ESLint check
pnpm test         # Run tests
pnpm test:run     # Run tests once
pnpm test:coverage # Test coverage
pnpm preview      # Cloudflare local preview
pnpm deploy       # Deploy to Cloudflare

Core Concepts โ€‹

Route File Conventions โ€‹

React Router 7 uses file-system routing:

_index.tsx        โ†’ /          (index route)
about.tsx         โ†’ /about     (static route)
users.tsx         โ†’ /users     (static route)
users.$id.tsx     โ†’ /users/:id (dynamic route)
_layout.tsx       โ†’ layout route
$.tsx             โ†’ splat route

Loader (Data Loading) โ€‹

tsx
// app/routes/users.tsx
import type { Route } from "./+types/users";

export async function loader({ request }: Route.LoaderArgs) {
  const url = new URL(request.url);
  const page = Number(url.searchParams.get("page")) || 1;

  const response = await fetch(`/api/users?page=${page}`);
  const users = await response.json();

  return { users, page };
}

export default function UsersPage({ loaderData }: Route.ComponentProps) {
  const { users, page } = loaderData;

  return (
    <div>
      <h1>User List</h1>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

Action (Form Handling) โ€‹

tsx
// app/routes/login.tsx
import type { Route } from "./+types/login";
import { Form, useActionData, useNavigation } from "react-router";

export async function action({ request }: Route.ActionArgs) {
  const formData = await request.formData();
  const email = formData.get("email");
  const password = formData.get("password");

  // Validation
  if (!email || !password) {
    return { error: "Please fill in all fields" };
  }

  // Login logic
  const response = await fetch("/api/auth/login", {
    method: "POST",
    body: JSON.stringify({ email, password }),
    headers: { "Content-Type": "application/json" },
  });

  if (!response.ok) {
    return { error: "Invalid email or password" };
  }

  // Redirect to homepage
  return redirect("/");
}

export default function LoginPage() {
  const actionData = useActionData<typeof action>();
  const navigation = useNavigation();
  const isSubmitting = navigation.state === "submitting";

  return (
    <Form method="post">
      {actionData?.error && (
        <p className="text-destructive">{actionData.error}</p>
      )}

      <input type="email" name="email" placeholder="Email" required />
      <input type="password" name="password" placeholder="Password" required />

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? "Logging in..." : "Login"}
      </button>
    </Form>
  );
}

Meta (TDK Meta Info) โ€‹

tsx
// app/routes/users.tsx
import type { Route } from "./+types/users";
import { generateMeta } from "~/lib/meta";

export function meta(): Route.MetaDescriptors {
  return generateMeta("/users");
}
ts
// app/lib/meta.ts
export const pageMetas: Record<string, PageMeta> = {
  "/users": {
    title: "User Management",
    description: "Manage system user accounts, including creation, editing, and permission configuration",
    keywords: ["user management", "account management", "permission configuration"],
  },
  // ...
};

export function generateMeta(path: string, overrides?: Partial<PageMeta>) {
  const meta = pageMetas[path] || { title: "Page", description: "" };
  // Return complete meta tag array
}

State Management โ€‹

Tabs Store (Tab Management) โ€‹

tsx
// app/stores/tabs-store.ts
import { create } from "zustand";
import { persist } from "zustand/middleware";

interface TabsState {
  tabs: Tab[];
  activeTabId: string | null;
  addTab: (tab: Omit<Tab, "id">) => string;
  removeTab: (id: string) => void;
  setActiveTab: (id: string) => void;
  clearTabs: () => void;
}

export const useTabsStore = create<TabsState>()(
  persist(
    (set, get) => ({
      tabs: [homeTab],
      activeTabId: "home",
      addTab: (tab) => { /* ... */ },
      removeTab: (id) => { /* ... */ },
      // ...
    }),
    { name: "tabs-storage" }
  )
);

UI Settings Store (Skin/Layout) โ€‹

tsx
// app/stores/ui-settings-store.ts
export type SkinPreset =
  | "default" | "blue" | "emerald" | "amber" | "violet"
  | "rose" | "teal" | "slate" | "ocean" | "sunset" | "aurora";

export const useUiSettingsStore = create<UiSettingsState>()(
  persist(
    (set) => ({
      skin: "default",
      showFooter: true,
      showTabBar: true,
      setSkin: (skin) => set({ skin }),
      setShowFooter: (visible) => set({ showFooter: visible }),
      // ...
    }),
    { name: "ui-settings-storage" }
  )
);

Theme System โ€‹

Skin Presets โ€‹

Supports 11 preset skins, switch via Quick Settings panel:

SkinPrimary Color
DefaultPurple
BlueBlue
EmeraldEmerald
AmberAmber
VioletViolet
RoseRose
TealTeal
SlateSlate
OceanOcean Blue
SunsetSunset Orange
AuroraAurora

CSS Variables (OKLch) โ€‹

css
/* app/app.css */
:root {
  --background: 100% 0 0;
  --foreground: 14.9% 0.017 285.75;
  --primary: 51.1% 0.262 276.97;
  --primary-foreground: 100% 0 0;
  /* ... */
}

[data-skin="ocean"] {
  --primary: 54.3% 0.195 240.03;
}

.dark {
  --background: 14.9% 0.017 285.75;
  --foreground: 98.5% 0 0;
  /* ... */
}

Page Routes โ€‹

PathPageDescription
/DashboardData overview + charts
/loginLoginUser login
/registerRegisterUser registration
/forgot-passwordForgot PasswordSend reset email
/reset-passwordReset PasswordSet new password
/usersUser ManagementUser list CRUD
/settingsSystem SettingsApp configuration
/profileProfileUser profile
/securitySecurity SettingsPassword change, etc.
/analyticsData AnalyticsChart display
/notificationsNotification CenterMessage list
/documentsDocument ManagementFile list
/calendarCalendarSchedule management

Testing โ€‹

Run Tests โ€‹

bash
pnpm test:run      # Single run
pnpm test          # Watch mode
pnpm test:coverage # Coverage report

Test Example โ€‹

tsx
// tests/stores/tabs-store.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import { useTabsStore } from "~/stores/tabs-store";

describe("useTabsStore", () => {
  beforeEach(() => {
    useTabsStore.getState().clearTabs();
  });

  it("should add new tab", () => {
    const { addTab } = useTabsStore.getState();
    addTab({ title: "User Management", path: "/users" });

    const { tabs } = useTabsStore.getState();
    expect(tabs).toHaveLength(2);
  });
});

Deployment โ€‹

Cloudflare Pages โ€‹

bash
pnpm deploy

Configure GitHub Secrets:

  • CLOUDFLARE_API_TOKEN
  • CLOUDFLARE_ACCOUNT_ID

Node.js Server โ€‹

bash
pnpm build
pnpm start

Docker โ€‹

dockerfile
FROM node:20-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 node:20-alpine
WORKDIR /app
COPY --from=builder /app/build ./build
COPY --from=builder /app/package.json .
RUN npm install --production
EXPOSE 3000
CMD ["npm", "start"]

CI/CD โ€‹

The project is configured with complete GitHub Actions CI workflow:

  • Lint & Type Check - ESLint + TypeScript checking
  • Unit Tests - Vitest unit tests + Codecov coverage
  • Build - Production build verification
  • Security Audit - Dependency security audit
  • Dependency Review - PR dependency change review

Comparison with Other Versions โ€‹

FeatureRemix VersionVue VersionNext.js Version
State ManagementZustandPiniaZustand
Data FetchingLoader/ActionTanStack QueryTanStack Query
Form HandlingProgressive Enhancement FormVeeValidateReact Hook Form
Server-sideBuilt-in SSRNuxtApp Router
Component LibraryRadix UIshadcn-vueshadcn/ui
RoutingFile-based routingVue RouterApp Router
ThemeOKLch CSS VariablesOKLch CSS VariablesOKLch CSS Variables
TestingVitestVitestVitest