Skip to content

Nuxt Version โ€‹

Halolight Nuxt version is built on Nuxt 3 with Vue 3.5 + Composition API + TypeScript, providing an out-of-the-box full-stack development experience.

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

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

Features โ€‹

  • ๐Ÿ”„ Auto Import - Components, composables, and APIs auto-imported with zero config
  • ๐Ÿ“ File-Based Routing - Automatic routing from the file system
  • ๐ŸŒ Full-Stack Development - Built-in Nitro server, unified frontend and backend
  • ๐Ÿš€ SSR/SSG/SPA - Flexible rendering modes
  • โšก Vite Powered - Lightning-fast HMR
  • ๐Ÿ”Œ Module Ecosystem - Rich Nuxt module extensions
  • ๐ŸŽจ Theme System - Light/dark theme toggle
  • ๐Ÿ” Authentication - Complete login/register/password recovery flow
  • ๐Ÿ“Š Dashboard - Data visualization and business management
  • ๐Ÿ›ก๏ธ Access Control - Fine-grained RBAC permission management
  • โŒ˜ Command Palette - โŒ˜/Ctrl + K quick navigation

Tech Stack โ€‹

TechnologyVersionDescription
Nuxt3.10Vue full-stack framework
Vue3.5+Progressive framework
TypeScript5.7Type safety
Tailwind CSS3.x (CDN)Atomic CSS
Pinia0.5State management
VueUse10.xComposables utility library
Mock.js1.xData mocking

Core Features โ€‹

  • Full-Stack Development: Built-in Nitro server for unified frontend and backend development
  • Auto Import: Automatic import of components, composables, and APIs
  • File-Based Routing: Automatic routing based on file system
  • SSR/SSG: Optional server-side rendering and static generation
  • Command Palette: โŒ˜/Ctrl + K for quick navigation
  • Hot Reload: Excellent HMR development experience
  • Module Ecosystem: Rich Nuxt module extensions

Directory Structure โ€‹

halolight-nuxt/
โ”œโ”€โ”€ nuxt.config.ts              # Nuxt configuration
โ”œโ”€โ”€ app.vue                     # App root component
โ”œโ”€โ”€ pages/                      # File-based routing
โ”‚   โ”œโ”€โ”€ index.vue              # Home page
โ”‚   โ”œโ”€โ”€ login.vue              # Login
โ”‚   โ”œโ”€โ”€ register.vue           # Register
โ”‚   โ”œโ”€โ”€ forgot-password.vue    # Forgot password
โ”‚   โ”œโ”€โ”€ reset-password.vue     # Reset password
โ”‚   โ”œโ”€โ”€ terms.vue              # Terms of service
โ”‚   โ”œโ”€โ”€ privacy.vue            # Privacy policy
โ”‚   โ”œโ”€โ”€ dashboard/             # Dashboard
โ”‚   โ”‚   โ””โ”€โ”€ index.vue
โ”‚   โ”œโ”€โ”€ users/                 # User management
โ”‚   โ”‚   โ””โ”€โ”€ index.vue
โ”‚   โ”œโ”€โ”€ messages/              # Messages
โ”‚   โ”‚   โ””โ”€โ”€ index.vue
โ”‚   โ”œโ”€โ”€ analytics/             # Analytics
โ”‚   โ”‚   โ””โ”€โ”€ index.vue
โ”‚   โ”œโ”€โ”€ profile/               # User profile
โ”‚   โ”‚   โ””โ”€โ”€ index.vue
โ”‚   โ””โ”€โ”€ settings/              # System settings
โ”‚       โ””โ”€โ”€ index.vue
โ”œโ”€โ”€ components/                 # Auto-imported components
โ”‚   โ””โ”€โ”€ common/                # Common components
โ”‚       โ”œโ”€โ”€ AppHeader.vue
โ”‚       โ”œโ”€โ”€ AppSidebar.vue
โ”‚       โ”œโ”€โ”€ AppFooter.vue
โ”‚       โ”œโ”€โ”€ AppTabs.vue
โ”‚       โ”œโ”€โ”€ CommandMenu.vue
โ”‚       โ””โ”€โ”€ ToastContainer.vue
โ”œโ”€โ”€ composables/                # Composable functions
โ”‚   โ”œโ”€โ”€ useTheme.ts
โ”‚   โ”œโ”€โ”€ useToast.ts
โ”‚   โ””โ”€โ”€ useCommandMenu.ts
โ”œโ”€โ”€ layouts/                    # Layouts
โ”‚   โ”œโ”€โ”€ default.vue            # Admin layout
โ”‚   โ””โ”€โ”€ auth.vue               # Auth layout
โ”œโ”€โ”€ middleware/                 # Middleware
โ”‚   โ””โ”€โ”€ auth.global.ts
โ”œโ”€โ”€ plugins/                    # Plugins
โ”‚   โ””โ”€โ”€ pinia-persist.client.ts
โ”œโ”€โ”€ stores/                     # Pinia stores
โ”‚   โ”œโ”€โ”€ auth.ts
โ”‚   โ”œโ”€โ”€ ui-settings.ts
โ”‚   โ”œโ”€โ”€ dashboard.ts
โ”‚   โ”œโ”€โ”€ layout.ts
โ”‚   โ””โ”€โ”€ tabs.ts
โ”œโ”€โ”€ utils/                      # Utility functions
โ”‚   โ”œโ”€โ”€ index.ts
โ”‚   โ””โ”€โ”€ mock.ts
โ”œโ”€โ”€ assets/css/                 # Style files
โ”‚   โ”œโ”€โ”€ main.css
โ”‚   โ””โ”€โ”€ tailwind.css
โ”œโ”€โ”€ tests/                      # Test files
โ”‚   โ””โ”€โ”€ unit/
โ”œโ”€โ”€ .github/                    # GitHub Actions
โ”‚   โ””โ”€โ”€ workflows/
โ”‚       โ”œโ”€โ”€ ci.yml
โ”‚       โ””โ”€โ”€ deploy.yml
โ””โ”€โ”€ public/                     # Static assets

Quick Start โ€‹

Requirements โ€‹

  • Node.js >= 18.0.0
  • pnpm >= 9.x

Installation โ€‹

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

Environment Variables โ€‹

bash
cp .env.example .env.local
env
# .env.local
NUXT_PUBLIC_API_BASE=/api
NUXT_PUBLIC_MOCK=true
NUXT_PUBLIC_DEMO_EMAIL=admin@halolight.h7ml.cn
NUXT_PUBLIC_DEMO_PASSWORD=123456
NUXT_PUBLIC_SHOW_DEMO_HINT=true
NUXT_PUBLIC_APP_TITLE=Admin Pro
NUXT_PUBLIC_BRAND_NAME=Halolight

Start Development โ€‹

bash
pnpm dev

Visit http://localhost:3000

Build for Production โ€‹

bash
pnpm build
pnpm preview

Demo Account โ€‹

RoleEmailPassword
Adminadmin@halolight.h7ml.cn123456
Useruser@halolight.h7ml.cn123456

Core Functionality โ€‹

State Management (Pinia) โ€‹

ts
// stores/auth.ts
export const useAuthStore = defineStore('auth', () => {
  const user = ref<User | null>(null)
  const token = ref('')
  const loading = ref(false)
  const error = ref('')

  const isAuthenticated = computed(() => !!token.value && !!user.value)

  async function login(credentials: LoginCredentials) {
    loading.value = true
    error.value = ''
    try {
      // Login logic
      const result = await mockLogin(credentials)
      user.value = result.user
      token.value = result.token
    } catch (e) {
      error.value = e.message
      throw e
    } finally {
      loading.value = false
    }
  }

  function logout() {
    user.value = null
    token.value = ''
    navigateTo('/login')
  }

  return { user, token, loading, error, isAuthenticated, login, logout }
})

Data Fetching (useFetch) โ€‹

vue
<script setup lang="ts">
// Using useFetch with automatic SSR handling
const { data: users, pending, error, refresh } = await useFetch('/api/users', {
  query: { page: 1, limit: 10 },
})

// Using useAsyncData with custom key
const { data: stats } = await useAsyncData('dashboard-stats', () =>
  $fetch('/api/dashboard/stats')
)
</script>

Access Control โ€‹

ts
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to) => {
  const authStore = useAuthStore()

  // Public pages list
  const publicPages = ['/login', '/register', '/forgot-password', '/reset-password']

  if (publicPages.includes(to.path)) {
    return
  }

  if (!authStore.isAuthenticated) {
    return navigateTo({
      path: '/login',
      query: { redirect: to.fullPath },
    })
  }
})

Draggable Dashboard โ€‹

vue
<script setup lang="ts">
// Dashboard configuration
const dashboardStore = useDashboardStore()
const widgets = computed(() => dashboardStore.widgets)

// Drag implementation
function handleDragEnd(event) {
  dashboardStore.updateLayout(event.newLayout)
}
</script>

Page Routes โ€‹

PathPagePermission
/HomePublic
/loginLoginPublic
/registerRegisterPublic
/forgot-passwordForgot passwordPublic
/reset-passwordReset passwordPublic
/termsTerms of servicePublic
/privacyPrivacy policyPublic
/dashboardDashboarddashboard:view
/usersUser managementusers:view
/messagesMessagesmessages:view
/analyticsAnalyticsanalytics:view
/profileUser profilesettings:view
/settingsSystem settingssettings:view

Environment Variables โ€‹

Configuration Example โ€‹

bash
# .env
NUXT_PUBLIC_API_BASE=/api
NUXT_PUBLIC_MOCK=true
NUXT_PUBLIC_DEMO_EMAIL=admin@halolight.h7ml.cn
NUXT_PUBLIC_DEMO_PASSWORD=123456
NUXT_PUBLIC_SHOW_DEMO_HINT=true
NUXT_PUBLIC_APP_TITLE=Admin Pro
NUXT_PUBLIC_BRAND_NAME=Halolight

# Server private variables
NUXT_JWT_SECRET=your-jwt-secret
NUXT_DATABASE_URL=postgresql://localhost:5432/halolight

Variable Description โ€‹

Variable NameDescriptionDefault
NUXT_PUBLIC_API_BASEAPI base URL/api
NUXT_PUBLIC_MOCKEnable mock datatrue
NUXT_PUBLIC_APP_TITLEApp titleAdmin Pro
NUXT_PUBLIC_BRAND_NAMEBrand nameHalolight
NUXT_PUBLIC_DEMO_EMAILDemo account email-
NUXT_PUBLIC_DEMO_PASSWORDDemo account password-
NUXT_JWT_SECRETJWT secret (server)-
NUXT_DATABASE_URLDatabase connection (server)-

Usage โ€‹

vue
<script setup lang="ts">
// In components
const config = useRuntimeConfig();

// Public variables
const apiBase = config.public.apiBase;

// Private variables (server only)
// const jwtSecret = config.jwtSecret; // Not accessible on client
</script>
typescript
// In server/api
export default defineEventHandler((event) => {
  const config = useRuntimeConfig();
  const jwtSecret = config.jwtSecret; // Can access private variables
});

Common Commands โ€‹

bash
# Development
pnpm dev              # Start dev server
pnpm dev -o           # Start and open browser

# Build
pnpm build            # Production build
pnpm preview          # Preview production build
pnpm generate         # Static site generation (SSG)

# Checks
pnpm typecheck        # TypeScript type check
pnpm lint             # ESLint check
pnpm lint:fix         # ESLint autofix

# Tests
pnpm test             # Run tests
pnpm test:run         # Single run
pnpm test:coverage    # Coverage report

# Nuxt CLI
npx nuxi add page <name>       # Add page
npx nuxi add component <name>  # Add component
npx nuxi add composable <name> # Add composable
npx nuxi add plugin <name>     # Add plugin
npx nuxi add middleware <name> # Add middleware
npx nuxi add api <name>        # Add API route
npx nuxi upgrade               # Upgrade Nuxt

Testing โ€‹

bash
pnpm test           # Run tests (watch mode)
pnpm test:run       # Single run
pnpm test:coverage  # Coverage report
pnpm test:ui        # Vitest UI

Test Example โ€‹

typescript
// tests/unit/stores/auth.spec.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useAuthStore } from '~/stores/auth'

describe('Auth Store', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('should have correct initial state', () => {
    const store = useAuthStore()
    expect(store.user).toBeNull()
    expect(store.token).toBe('')
    expect(store.isAuthenticated).toBe(false)
  })

  it('should login successfully', async () => {
    const store = useAuthStore()
    await store.login({ email: 'admin@halolight.h7ml.cn', password: '123456' })
    expect(store.isAuthenticated).toBe(true)
  })
})

Configuration โ€‹

Nuxt Configuration โ€‹

ts
// nuxt.config.ts
export default defineNuxtConfig({
  compatibilityDate: '2025-11-30',
  devtools: { enabled: false },

  modules: [
    '@pinia/nuxt',
    '@vueuse/nuxt',
  ],

  css: ['~/assets/css/main.css'],

  runtimeConfig: {
    public: {
      apiBase: '/api',
      mock: true,
      demoEmail: 'admin@halolight.h7ml.cn',
      demoPassword: '123456',
      showDemoHint: true,
      appTitle: 'Admin Pro',
      brandName: 'Halolight',
    },
  },

  app: {
    head: {
      title: 'Admin Pro',
      script: [
        { src: 'https://cdn.tailwindcss.com' },
      ],
    },
  },
})

Deployment โ€‹

bash
npx vercel

Or use the Vercel button for one-click deployment:

Deploy with Vercel

Docker โ€‹

dockerfile
FROM node:20-alpine AS builder

RUN npm install -g pnpm

WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

COPY . .
RUN pnpm build

FROM node:20-alpine AS runner

WORKDIR /app

COPY --from=builder /app/.output ./.output

ENV HOST=0.0.0.0
ENV PORT=3000

EXPOSE 3000

CMD ["node", ".output/server/index.mjs"]

Other Platforms โ€‹

  • Cloudflare Pages: Configure nitro.preset: 'cloudflare-pages'
  • Netlify: Configure nitro.preset: 'netlify'
  • Node.js Server: pnpm build && node .output/server/index.mjs

CI/CD โ€‹

Complete GitHub Actions CI workflow configured:

yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm typecheck

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm test:coverage
      - uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm build

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm audit --audit-level=high

Advanced Features โ€‹

Server Routes (API Endpoints) โ€‹

Nuxt 3 has a built-in Nitro server for creating server-side APIs.

typescript
// server/api/users/index.get.ts
export default defineEventHandler(async (event) => {
  const query = getQuery(event);
  const { page = 1, limit = 10 } = query;

  // Mock database query
  const users = await getUsersFromDB({ page: Number(page), limit: Number(limit) });

  return {
    success: true,
    data: users,
    pagination: {
      page: Number(page),
      limit: Number(limit),
      total: 100,
    },
  };
});

Server Middleware โ€‹

typescript
// server/middleware/auth.ts
export default defineEventHandler((event) => {
  const url = getRequestURL(event);

  // Protect API routes
  if (url.pathname.startsWith('/api/admin')) {
    const token = getHeader(event, 'authorization')?.replace('Bearer ', '');

    if (!token) {
      throw createError({
        statusCode: 401,
        message: 'Unauthorized',
      });
    }

    try {
      const user = verifyToken(token);
      event.context.user = user;
    } catch {
      throw createError({
        statusCode: 401,
        message: 'Invalid or expired token',
      });
    }
  }
});

Plugins โ€‹

typescript
// plugins/api.ts
export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig();

  const api = $fetch.create({
    baseURL: config.public.apiBase,
    onRequest({ options }) {
      const authStore = useAuthStore();
      if (authStore.token) {
        options.headers = {
          ...options.headers,
          Authorization: `Bearer ${authStore.token}`,
        };
      }
    },
    onResponseError({ response }) {
      if (response.status === 401) {
        const authStore = useAuthStore();
        authStore.logout();
        navigateTo('/login');
      }
    },
  });

  return {
    provide: {
      api,
    },
  };
});

Composables โ€‹

typescript
// composables/useUsers.ts
export function useUsers() {
  const { $api } = useNuxtApp();

  const users = ref<User[]>([]);
  const loading = ref(false);
  const error = ref<string | null>(null);

  async function fetchUsers(params?: { page?: number; limit?: number }) {
    loading.value = true;
    error.value = null;

    try {
      const response = await $api<ApiResponse<User[]>>('/api/users', {
        params,
      });
      users.value = response.data;
      return response;
    } catch (e: any) {
      error.value = e.message;
      throw e;
    } finally {
      loading.value = false;
    }
  }

  return {
    users,
    loading,
    error,
    fetchUsers,
  };
}

Performance Optimization โ€‹

Image Optimization โ€‹

vue
<script setup lang="ts">
// Using @nuxt/image
</script>

<template>
  <NuxtImg
    src="/hero.jpg"
    alt="Hero"
    width="800"
    height="600"
    loading="lazy"
    format="webp"
  />

  <NuxtPicture
    src="/hero.jpg"
    alt="Hero"
    width="800"
    height="600"
    sizes="sm:100vw md:50vw lg:33vw"
  />
</template>

Lazy-Load Components โ€‹

vue
<script setup lang="ts">
// Lazy-load heavy components
const Chart = defineAsyncComponent(() => import('~/components/Chart.vue'));
</script>

<template>
  <ClientOnly>
    <Chart :data="chartData" />
    <template #fallback>
      <div class="h-80 animate-pulse bg-gray-200" />
    </template>
  </ClientOnly>
</template>

Prefetch โ€‹

vue
<script setup lang="ts">
// Prefetch critical data
const { data } = await useFetch('/api/critical-data', {
  key: 'critical',
  lazy: false,
});
</script>

Frequently Asked Questions โ€‹

Q: How do I configure SSG (static generation)? โ€‹

A: Update nuxt.config.ts:

typescript
export default defineNuxtConfig({
  ssr: true,
  nitro: {
    prerender: {
      routes: ['/', '/about', '/contact'],
      crawlLinks: true,
    },
  },
});

Run pnpm generate to create the static site.

Q: How do I configure SPA mode? โ€‹

A: Disable SSR:

typescript
export default defineNuxtConfig({
  ssr: false,
});

Q: What is the difference between useFetch and $fetch? โ€‹

A:

  • useFetch is a composable that automatically handles SSR data syncing
  • $fetch is the low-level method and does not handle SSR
vue
<script setup lang="ts">
// Recommended: handles SSR automatically
const { data } = await useFetch('/api/users');

// Manual call
const fetchData = async () => {
  const data = await $fetch('/api/users');
};
</script>

Q: How do I add global CSS? โ€‹

A: Configure it in nuxt.config.ts:

typescript
export default defineNuxtConfig({
  css: ['~/assets/css/main.css'],
});

Q: How do I configure a proxy? โ€‹

A: Use nitro.routeRules:

typescript
export default defineNuxtConfig({
  nitro: {
    routeRules: {
      '/api/external/**': {
        proxy: 'https://api.example.com/**',
      },
    },
  },
});

Q: How do I implement authentication? โ€‹

A: Use middleware + Pinia:

typescript
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to) => {
  const authStore = useAuthStore();
  const publicPages = ['/login', '/register', '/forgot-password'];

  if (!publicPages.includes(to.path) && !authStore.isAuthenticated) {
    return navigateTo({
      path: '/login',
      query: { redirect: to.fullPath },
    });
  }
});

Q: How do I optimize bundle size? โ€‹

A: Optimization suggestions:

typescript
export default defineNuxtConfig({
  // On-demand component import
  components: {
    dirs: ['~/components'],
    global: false,
  },

  // Experimental features
  experimental: {
    treeshakeClientOnly: true,
  },

  // Vite optimization
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            'vue-vendor': ['vue', 'vue-router', 'pinia'],
          },
        },
      },
    },
  },
});

Comparison with Other Versions โ€‹

FeatureNuxt VersionVue VersionNext.js Version
State ManagementPiniaPiniaZustand
Data FetchinguseFetchAxiosTanStack Query
Form ValidationNativeVeeValidate + ZodReact Hook Form + Zod
ServerBuilt-in NitroSeparate backendAPI Routes
StylingTailwind CDNTailwindTailwind
RoutingFile-based routingVue RouterApp Router
SSRBuilt-in supportRequires configurationBuilt-in support