Getting Started
Install the template, run it locally, and understand the project layout in a few minutes.
Overview
Luminaux is a production-style React admin template. It ships as a real
server-rendered application wired to a REST API client, with a fully configurable shell, 13 visual skins, dark mode, internationalization, and a large component + application library. This guide gets you from a fresh clone to a running dev server.
Key benefits
- Modern, strict-TypeScript codebase (React 19, Next 16 App Router).
- Server-rendered first paint — the visitor's theme, skin and layout arrive in the first byte, with no flash.
- Zero-config Tailwind CSS v4 (CSS-first, no
tailwind.config.js). - Everything is token-driven and themeable — no hardcoded colors.
- Real applications (chat, calendar, kanban, contacts, invoice builder) you can adapt, not throwaway demos — plus complete auth, settings, email-template, error, cookie-consent, and FAQ page sets.
Tech stack
| Concern | Choice |
|---|---|
| Framework | React 19 + TypeScript 5.7 (strict) |
| Build tool | Next 16 App Router (Turbopack) + @tailwindcss/postcss |
| Styling | Tailwind CSS v4 (CSS-first) + a small SCSS structural layer |
| Routing | App Router file-system routes under a [locale] segment |
| i18n | next-intl (ICU messages, server-rendered) |
| Rendering | SSR — server components, Server Actions, Route Handlers |
| Icons | lucide-react |
| Class composition | clsx + tailwind-merge (cn() in src/lib/cn.ts) |
| Animation | Motion (motion/react) — free tier only |
| Data grid | AG Grid Community v36 |
| Charts | Recharts, ApexCharts, ECharts, Chart.js (react-chartjs-2) |
| Calendar | FullCalendar v6 (MIT plugins + rrule) |
| Drag & drop | @dnd-kit (core / sortable / utilities) |
| Forms select | react-select |
| Form pickers | react-datepicker, react-colorful |
| Rich text | Tiptap (@tiptap/react + starter-kit & extensions) |
| Spreadsheet export | exceljs (lazy-loaded, styled .xlsx from the invoice builder) |
| Emoji | emoji-mart |
See Architecture & Routing for how these fit together, and the individual guides for per-feature dependencies.
Prerequisites
- Node.js 20+ (20 LTS or newer recommended).
- npm (the repo ships a
package-lock.json). Yarn/pnpm work too, but examples use npm.
Installation
# 1. Install dependencies
npm install
# 2. Start the dev server (http://localhost:3000)
npm run dev
# 3. Build for production (type-checks, then builds the server + client bundles)
npm run build
# 4. Run the production server locally
npm startThe dev server runs on http://localhost:3000 (Next will pick the next free port if it's taken).
dev and build each run a codegen step first (npm run codegen), which compiles the translation
files into ICU message bundles and refreshes the generated route files. It is wired as predev/prebuild,
so you never have to invoke it yourself.
NPM scripts
| Script | Command | Purpose |
|---|---|---|
dev | next dev | Start the hot-reloading dev server (runs codegen first) |
build | next build | Produce the production server + client bundles (runs codegen first) |
start | next start | Serve the production build |
codegen | build-messages + gen-routes | Compile ICU message bundles and refresh the generated route files |
lint | eslint . | Run ESLint (typescript-eslint + react-hooks + react-refresh) |
format | prettier --write . | Auto-format the whole repo |
format:check | prettier --check . | Verify formatting (use in CI) |
i18n:translate | node --env-file=.env tools/translate/translate.mjs | Fill all target languages from en/ via DeepL — multi-key rotation, resumable, scoped with --lang/--ns (needs DEEPL_API_KEY) |
i18n:usage | node --env-file=.env tools/translate/check-usage.mjs | Report each DeepL key's remaining monthly character quota (free — no characters billed) |
Info
✅ Always run npm run build before considering a change done. Strict mode
(noUnusedLocals/noUnusedParameters) fails the build on unused imports.
Environment variables
Create a .env file at the repo root as needed:
# REST API base URL used by the HTTP client (src/lib/api.ts).
# Defaults to "/api" when unset.
NEXT_PUBLIC_API_BASE_URL=https://api.example.com
# Absolute site URL — used for sitemap.xml, robots.txt and OpenGraph URLs.
NEXT_PUBLIC_SITE_URL=https://example.com
# Route guard. OFF by default so every page stays browsable; set to true to
# require a session cookie for the app routes.
AUTH_REQUIRED=false
# Required only for the translation tool (npm run i18n:translate).
# One key, or several for rotation — a comma-separated list and/or numbered vars.
# See documentation/reference/translation-tool.md.
DEEPL_API_KEY=your-deepl-key
# DEEPL_API_KEY_2=second-key
# DEEPL_API_KEY_3=third-keyOnly variables prefixed with NEXT_PUBLIC_ are exposed to the browser; everything else stays
server-only, which is why AUTH_REQUIRED has no prefix. The API client reads
NEXT_PUBLIC_API_BASE_URL — see
Utilities & API Client. A copy of every variable with
inline notes lives in .env.example.
Project structure
src/
├── components/
│ ├── ui/ # Reusable presentational primitives (barrel: index.ts)
│ ├── motion/ # Animation wrappers & FX (barrel: index.ts)
│ ├── auth/ # Auth-screen kit (AuthLayout, AuthCard, PasswordField, …)
│ ├── chat/ # Chat feature
│ ├── calendar/ # Calendar feature
│ ├── scrumboard/ # Kanban feature
│ ├── contacts/ # Applicant-review feature
│ ├── cookies/ # Cookie-consent kit (banner, preferences modal, useConsent)
│ ├── invoice/ # Invoice builder (form + live preview)
│ ├── email/ # Email-template browser pieces
│ ├── faq/ # FAQ page sections
│ ├── widgets/ # Widgets gallery (+ primitives/)
│ ├── editable/ # Inline-editable form pieces
│ ├── datagrid/ # AG Grid helpers
│ ├── bento/ # Bento dashboard tiles
│ ├── pricing/ # Pricing page sections
│ ├── settings/ # Settings hub shell + Layout Settings ShellPreview
│ ├── loading/ # Splash + route loader
│ └── layout/ # Grid (Row/Col)
├── context/ # LayoutContext (shell config, skin, OLED)
├── data/ # Seed data + persistence (menu, chat, calendar, invoice, cookies, emailTemplates/, …)
├── hooks/ # Shared hooks (useTheme, usePresence, useChartTokens, …)
├── i18n/ # i18n configuration + language list
├── layout/ # App shell: AppLayout, Sidebar, Header, PageHeader, overlays, mega-nav
├── lib/ # Utilities: cn, api, motion, csv, ics, appStorage, customizerOptions, …
├── locales/ # Translation JSON per language/namespace
├── pages/ # Route screens (dashboards, ui, forms, tables, charts, apps, layouts,
│ # auth, email, errors, settings, cookies)
├── styles/ # index.css (Tailwind + tokens) + main.scss (structural SCSS partials)
├── config/ # brand.ts
├── app/ # App Router tree — [locale]/(app)/**, [locale]/(auth)/**, api/**
├── platform/ # Framework adapters (nav, i18n, env, routes, auth, image, clientOnly)
├── i18n/ # next-intl request configuration
└── proxy.ts # Locale resolution + the optional auth guard
messages/ # Generated ICU message bundles, one per locale — do not edit by handEverything above app/ is shared, framework-agnostic code — the same components, pages, hooks and
locale files the React edition uses. The Next-specific surface is deliberately small: the route tree,
seven platform adapters, and the request-level i18n and proxy configuration.
For the full explanation of the routing model and provider tree, read Architecture & Routing.
Adding a new page
Adding a real page takes three edits (the menu and router can't drift — any menu leaf without a route auto-renders a placeholder):
-
Create the route file at
src/app/[locale]/(app)/reports/page.tsx. Routing is file-system based, so the folder path is the URL:// src/app/[locale]/(app)/reports/page.tsx import {ReportsPage} from '@/pages/reports/ReportsPage' export const metadata = {title: 'Reports'} export default function Page() { return <ReportsPage /> } -
Add the nav item in
src/data/menu.tsso it appears in the sidebar:{key: 'reports', label: 'Reports', to: '/reports'} -
Add the i18n label
nav:reportstosrc/locales/en/nav.json:{ "reports": "Reports" }
That's it — the page renders inside the shell with a <PageHeader>. The (app) group supplies the
shell; the [locale] segment is applied for you, so link to /reports and never to /en/reports.
Info
Standalone pages (no shell): pages that must render outside AppLayout — like the seven /auth/*
screens — go in the (auth) route group instead, which has its own bare layout. See
Architecture & Routing → Standalone auth routes.
Server or client?
A page that reads browser storage, uses a chart library, or otherwise needs the
DOM should be reached through @/platform/clientOnly rather than rendered on the server. See
Architecture & Routing.
Coding conventions
Formatting and linting are enforced by tooling — don't hand-fight them.
- Prettier (
.prettierrc.json): no semicolons, single quotes, 4-space indent, tight import braces (bracketSpacing: false), width 110, trailing commas. Runnpm run format. - ESLint (
eslint.config.js, flat config): typescript-eslint + react-hooks + react-refresh. - TypeScript strict with
noUnusedLocals/noUnusedParameters— keep imports clean or the build fails. - Function components with named exports (except
App). Props typed asinterface XxxProps. - Import order: React → libs →
@/…→ relative. Path alias@/maps tosrc/. - The three golden rules: colors → semantic tokens only; user-facing text → i18n keys; motion values →
src/lib/motion.tstokens.
Troubleshooting
| Symptom | Fix |
|---|---|
npm run build fails on "declared but never read" | Remove the unused import/variable — strict mode forbids them. |
Dev server not on 3000 | Next picks the next free port when 3000 is busy; check the terminal output. |
| API calls 401 / no auth | Set NEXT_PUBLIC_API_BASE_URL. Sessions use an httpOnly cookie, so a token in localStorage is not read. |
| Redirected to the login page unexpectedly | AUTH_REQUIRED=true turns on the route guard. Unset it (or set false) to browse every page freely. |
| A page errors with "window is not defined" | It touches the DOM during render. Reach it through @/platform/clientOnly instead of importing it directly. |
| Translations show the raw key | The ICU bundles are stale — re-run npm run codegen. |
| Styles look unthemed / transparent in SCSS | SCSS must use raw token vars (var(--surface)), not --color-* aliases. See Design Tokens. |
| "file modified since read" while editing | The IDE/linter rewrote the file — re-read before editing. |
FAQ
Do I need a backend to run the template? No. It runs standalone; app features (chat, calendar, etc.)
persist to localStorage. Point
NEXT_PUBLIC_API_BASE_URL
at your API when you're ready to wire real data.
Is there a tailwind.config.js? No — Tailwind v4 is configured CSS-first inside src/styles/index.css
via @theme. See Design Tokens & Dark Mode.
Which languages are translated out of the box? en and ja (full key parity across all 24 namespaces);
de/fr are partial. Run npm run i18n:translate to fill the rest — see
Internationalization.
Notes for designers & content editors
- You never need to edit component logic to re-color the template — change tokens (see Design Tokens) or switch a skin (see Design Skins).
- All visible text lives in
src/locales/<lang>/*.jsonas flat keys. Editing copy = editing JSON. - Use the in-app Layout Settings page and the header gear (Customizer) to explore layouts live before committing to a configuration.
Related
Was this page helpful?
