Vela — Nuxt Admin Dashboard Template
Thank you for purchasing Vela! This guide covers installation, customization, and the full architecture of the Nuxt.js edition of the template.
Introduction
This is the Nuxt.js edition of Vela — the same Vue 3 component library and ~155-page design as the Vue.js edition, running under Nuxt 4 with full server-side rendering. Built with Vue 3 (<script setup> + TypeScript), Nuxt's file-based router, Tailwind CSS v4, and Vite. It ships with:
- 10 complete demo dashboards — Analytics, CRM, Ecommerce, Finance, Sales, Marketing, Logistics, Projects, SaaS, and Business Intelligence.
- ~155 production-ready routes, all server-rendered — full CRUD flows, apps (chat, email, calendar, kanban…), tables, forms, charts, auth flows, settings, reports and utility pages.
- The same 27 UI components + 9 chart components as the Vue.js edition, shared verbatim.
- Dark & light themes that render correctly on the very first server-sent byte — no theme flash, no hydration mismatch.
- No tracking scripts — the template contains no analytics, no external calls except Google Fonts and demo images.
Requirements
- Node.js 20+ (LTS recommended) — nodejs.org
- npm 10+ (bundled with Node)
- Any modern editor — VS Code with the Vue - Official (Volar) extension recommended.
- A hosting environment that can run a Node.js server (or any platform with Nitro's edge/serverless presets — see Build & deployment).
Installation
Unzip the package, open a terminal inside the vela-nuxt folder, then:
# 1. Install dependencies
npm install
# 2. Start the dev server (http://localhost:3000)
npm run dev
# 3. Type-check
npm run typecheck
# 4. Production build (outputs to .output/)
npm run build
# 5. Preview the production build locally
node .output/server/index.mjs
app/data.Project structure
vela-nuxt/
├─ nuxt.config.ts # Nuxt config: components auto-import, Tailwind Vite plugin
├─ public/ # Static assets, favicon, this documentation
└─ app/ # Nuxt 4's srcDir (@ and ~ both alias here)
├─ assets/css/main.css # ALL design tokens + Tailwind theme mapping
├─ pages/ # Real Nuxt route tree — thin wrapper files only
├─ components/
│ ├─ pages/ # The actual page implementations (one per route)
│ ├─ ui/ # 27 reusable components (Button, Card, DataTable…)
│ ├─ charts/ # 9 handcrafted SVG chart components
│ └─ layout/ # AppShell chrome: Sidebar, Topbar, MobileDrawer…
├─ layouts/ # default.vue (app shell), auth.vue
├─ composables/ # useTheme, useToast (SSR-safe, see below)
├─ data/ # Local demo data (typed, no backend)
├─ lib/ # Helpers (cn, status)
└─ router/paths.ts # Canonical URL manifest for every page
components/pages/, not pages/. The real pages/ tree is a set of thin wrapper files (<template><AnalyticsDashboardPage /></template>) so Nuxt's flat component auto-import can resolve every page component by filename alone, with zero import statements.Routing & adding pages
Every URL still lives in one manifest: app/router/paths.ts (identical to the Vue.js edition). To add a page:
1. Register the path
// app/router/paths.ts
reports: {
root: "/reports",
weekly: "/reports/weekly", // ← new
},
2. Create the page implementation
// app/components/pages/reports/WeeklyReportPage.vue
<script setup lang="ts">
// ...
</script>
<template>...</template>
3. Add the thin route wrapper
// app/pages/reports/weekly.vue
<template>
<WeeklyReportPage />
</template>
4. Add it to the sidebar
// app/components/layout/nav-config.ts — find the group and add a leaf
{ label: "Weekly Report", to: paths.reports.weekly },
app/pages/ — a [id].vue file segment maps to a :id route param, and index.vue maps to its parent folder's own path.UI & chart components
nuxt.config.ts registers flat, non-prefixed auto-imports for components/{pages,ui,charts,layout}, so every component resolves by its own filename with no import statement needed, anywhere in the app:
<Button variant="primary" size="sm">Save changes</Button>
<DataTable :rows="rows" :columns="columns" />
Highlights: DataTable (generic, sortable, custom per-column cells via named scoped slots keyed by column key), Kanban, Timeline, Modal/Drawer/Popover (each wrapped in <ClientOnly> around a <Teleport to="body"> — see SSR notes), form controls (Input, Select, Textarea, Checkbox, Radio, each using defineModel() for real two-way binding), Skeleton loaders, and AnimatedNumber. A live gallery of every component ships in the app itself at /components.
Charts (components/charts) are dependency-free SVG: area, bar, donut, heatmap, funnel, sparkline, gauge, and Gantt — all consuming the same design tokens, no charting library required. Any id generated at render time (e.g. gradient ids) uses Vue's SSR-safe useId(), never Math.random().
Colors & theming
The entire look of Vela is controlled from one file: app/assets/css/main.css. It defines raw CSS variables for dark (:root) and light (:root[data-theme="light"]), then maps them into Tailwind utilities via the @theme block — identical token set to every other Vela edition.
Changing the accent color
Update --acc, --acc-2, and --acc-soft in both the dark and light blocks. Every button, chart, link, and gradient updates instantly — components only ever reference tokens, never raw hex values.
Dark / light mode
app/composables/useTheme.ts reads and writes the theme via Nuxt's useCookie() (not localStorage) and applies data-theme to <html> via useHead(). This is what lets the correct theme render on the first server-sent byte — see SSR & hydration notes for why this matters. Use it anywhere:
const { theme, toggleTheme } = useTheme();
State management
Vela intentionally ships with no external state library — no Pinia, no Vuex — so you can drop in whatever your product uses. State is organized as:
- Global UI state —
useThemeanduseToast, both built on Nuxt'suseState()(request-scoped, SSR-safe) rather than a plain module-level ref, so concurrent requests on the server never leak state between users. - Page state — plain
ref/computedinside each page (filters, pagination, modals…). - Demo data — typed constants in
app/data/*.ts. Replace these with real API calls (e.g. viauseFetch/useAsyncData); page components consume plain arrays/objects, so swapping the data source is straightforward.
SSR & hydration notes
This edition renders on the server by default (ssr: true). A few patterns in the codebase exist specifically to keep server-rendered HTML and the client's first render in agreement, and are worth knowing if you extend the app:
Never read browser-only state during render
localStorage, window, and document don't exist on the server. Reading them at component setup time (rather than inside onMounted() or an event handler) makes the server render one thing and the client render another — Vue then reports a hydration mismatch. This is exactly what the theme composable used to do during development; it now sources the theme from useCookie() instead, which is visible to the server on the incoming request.
Never generate a random value at render time
Math.random(), crypto.randomUUID(), and bare Date.now() called during setup/render produce a different value on the server than on the client's re-render. Use Vue's useId() for any id that needs to be unique but stable across SSR and hydration (see AreaLineChart.vue's gradient id for a worked example). It's fine to call these inside a click handler or other user-triggered function — only render-time calls are unsafe.
Wrap body-teleporting overlays in <ClientOnly>
Modal, Drawer, ToastHost, and CommandPalette all use <Teleport to="body">. Wrapping them in <ClientOnly> is a zero-cost safeguard — they're closed by default anyway, so nothing is visible pre-hydration either way.
<body>, follow the same patterns: request-scoped state via useState(), no direct DOM/storage reads outside onMounted/handlers, and <ClientOnly> around the teleport.Other framework versions
Vela ships as one multi-framework product. If this Nuxt.js edition isn't the right fit for your stack, the same ~155-page design and demo data are also available as:
- Vue.js — this same component library, client-rendered only (no Node.js server to run in production).
- React and Next.js editions.
- Static HTML/CSS/vanilla-JS — no build step, no framework at all.
- Laravel, Django, and ASP.NET editions, generated from the static HTML export for teams standardized on those backends.
Each edition is a separate downloadable package with its own bundled documentation.
Build & deployment
npm run build produces a Nitro server bundle in .output/. Unlike the Vue.js (SPA) edition, this is a real server — run it with node .output/server/index.mjs, or target one of Nitro's built-in deployment presets for your platform:
| Target | How |
|---|---|
| Node.js server (any host/VPS) | Default build output — node .output/server/index.mjs |
| Vercel / Netlify | Auto-detected by the platform's Nuxt integration — no config needed |
| Static hosting (no SSR) | nuxt generate pre-renders every route to static HTML if you don't need per-request SSR |
See the official Nuxt deployment docs for the full list of Nitro presets (Cloudflare, Deno, AWS Lambda, and more).
Credits & licenses
All third-party software used by Vela is free, open-source, and licensed for commercial use:
| Package | Purpose | License |
|---|---|---|
| Nuxt | Application framework / SSR | MIT |
| Vue 3 | UI runtime | MIT |
| Tailwind CSS (+ Vite plugin) | Styling | MIT |
| Vite | Build tool | MIT |
| TypeScript | Language (dev only) | Apache-2.0 |
Fonts
| Font | Source | License |
|---|---|---|
| Plus Jakarta Sans | Google Fonts | SIL Open Font License 1.1 |
| JetBrains Mono | Google Fonts | SIL Open Font License 1.1 |
Images & icons
- All icons and illustrations are hand-drawn inline SVG created for this template — no icon font or icon library is bundled.
- Demo product photos are hot-linked from Unsplash (free for commercial use, no attribution required). They are demo placeholders — replace them with your own product imagery.
Support
If you have questions not covered here, please reach out through the item's Comments tab or the support contact listed on our ThemeForest profile. Please include your purchase code and a description of the issue.
© Vela. All rights reserved.