Nuxt Guide
Folder Structure
Project folder layout, nuxt.config.ts baseline, CSS setup, and app.vue structure.
Folder Structure
app/
├── app.vue ← Root component — wraps <NuxtLayout> in <ClientOnly>
├── assets/
│ └── css/
│ └── main.css ← @import 'tailwindcss'; @plugin 'daisyui';
├── components/ ← PascalCase.vue components; auto-imported by Nuxt
├── composables/ ← useXDomain() factory functions; auto-imported
├── layouts/
│ └── default.vue ← Main app layout (sidebar drawer or navbar)
├── middleware/
│ ├── auth.ts ← Named — redirects to /login if no token
│ ├── tenant.global.ts ← Global — runs on every route
│ └── 01.org.ts ← Ordered global — numeric prefix controls order
├── pages/ ← File-based routing; <script setup> + definePageMeta
├── plugins/
│ ├── api.ts ← $api: typed $fetch with auth headers + 401 redirect
│ ├── auth.client.ts ← Bootstraps currentUser on app load
│ ├── socket.client.ts ← Provides $socket with room helpers
│ └── phosphor-icons.ts ← Registers icon components globally
├── types/
│ ├── index.ts ← Barrel export
│ ├── auth.ts ← IUser, IUserType, IUserStatus
│ ├── resource.ts ← IResource, IResourceStatus
│ └── form.ts ← IValidationRule, IApiActionOptions
├── public/
│ └── robots.txt
├── nuxt.config.ts
└── tsconfig.json
nuxt.config.ts Baseline
import tailwindcss from '@tailwindcss/vite'
export default defineNuxtConfig({
ssr: false, // SPA mode
compatibilityDate: '2025-05-15',
nitro: {
preset: 'cloudflare-pages',
devProxy: { host: '0.0.0.0' },
},
vite: {
plugins: [tailwindcss()],
},
css: ['~/assets/css/main.css'],
modules: [
'nuxt-phosphor-icons',
['@nuxtjs/google-fonts', {
families: { Geist: true, Inter: [400, 700] },
}],
],
// All API calls are proxied through Nitro — browser never sees the real API URL
routeRules: {
'/api/auth/**': { proxy: `${process.env.API_CORE}/api/auth/**` },
'/api/resources/**': { proxy: `${process.env.API_CORE}/api/resources/**` },
'/api/users/**': { proxy: `${process.env.API_CORE}/api/users/**` },
},
runtimeConfig: {
public: {
cookieConfig: {
maxAge: 60 * 60 * 24 * 7,
secure: true,
sameSite: 'lax' as const,
},
socketUrl: '',
},
},
})
CSS Setup
DaisyUI v5 is loaded as a Tailwind CSS v4 plugin — no tailwind.config.js needed.
/* app/assets/css/main.css */
@import 'tailwindcss';
@plugin 'daisyui';
Themes are configured via the @plugin directive:
@import 'tailwindcss';
@plugin 'daisyui' {
themes: light --default, dark --prefersdark;
}
app.vue Structure
Because SSR is disabled, all content is wrapped in <ClientOnly> to prevent hydration mismatches:
<!-- app/app.vue -->
<template>
<div>
<ClientOnly>
<NuxtLoadingIndicator />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</ClientOnly>
</div>
</template>