PVR Tech Studio

Getting Started

Install the template, run it locally, and understand the project layout in a few minutes.

7 min read
Updated August 13, 2026

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

ConcernChoice
FrameworkReact 19 + TypeScript 5.7 (strict)
Build toolNext 16 App Router (Turbopack) + @tailwindcss/postcss
StylingTailwind CSS v4 (CSS-first) + a small SCSS structural layer
RoutingApp Router file-system routes under a [locale] segment
i18nnext-intl (ICU messages, server-rendered)
RenderingSSR — server components, Server Actions, Route Handlers
Iconslucide-react
Class compositionclsx + tailwind-merge (cn() in src/lib/cn.ts)
AnimationMotion (motion/react) — free tier only
Data gridAG Grid Community v36
ChartsRecharts, ApexCharts, ECharts, Chart.js (react-chartjs-2)
CalendarFullCalendar v6 (MIT plugins + rrule)
Drag & drop@dnd-kit (core / sortable / utilities)
Forms selectreact-select
Form pickersreact-datepicker, react-colorful
Rich textTiptap (@tiptap/react + starter-kit & extensions)
Spreadsheet exportexceljs (lazy-loaded, styled .xlsx from the invoice builder)
Emojiemoji-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 start

The 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

ScriptCommandPurpose
devnext devStart the hot-reloading dev server (runs codegen first)
buildnext buildProduce the production server + client bundles (runs codegen first)
startnext startServe the production build
codegenbuild-messages + gen-routesCompile ICU message bundles and refresh the generated route files
linteslint .Run ESLint (typescript-eslint + react-hooks + react-refresh)
formatprettier --write .Auto-format the whole repo
format:checkprettier --check .Verify formatting (use in CI)
i18n:translatenode --env-file=.env tools/translate/translate.mjsFill all target languages from en/ via DeepL — multi-key rotation, resumable, scoped with --lang/--ns (needs DEEPL_API_KEY)
i18n:usagenode --env-file=.env tools/translate/check-usage.mjsReport each DeepL key's remaining monthly character quota (free — no characters billed)

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-key

Only 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 hand

Everything 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):

  1. 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 />
    }
  2. Add the nav item in src/data/menu.ts so it appears in the sidebar:

    {key: 'reports', label: 'Reports', to: '/reports'}
  3. Add the i18n label nav:reports to src/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.

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. Run npm 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 as interface XxxProps.
  • Import order: React → libs → @/… → relative. Path alias @/ maps to src/.
  • The three golden rules: colors → semantic tokens only; user-facing text → i18n keys; motion values → src/lib/motion.ts tokens.

Troubleshooting

SymptomFix
npm run build fails on "declared but never read"Remove the unused import/variable — strict mode forbids them.
Dev server not on 3000Next picks the next free port when 3000 is busy; check the terminal output.
API calls 401 / no authSet NEXT_PUBLIC_API_BASE_URL. Sessions use an httpOnly cookie, so a token in localStorage is not read.
Redirected to the login page unexpectedlyAUTH_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 keyThe ICU bundles are stale — re-run npm run codegen.
Styles look unthemed / transparent in SCSSSCSS must use raw token vars (var(--surface)), not --color-* aliases. See Design Tokens.
"file modified since read" while editingThe 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>/*.json as 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.

Was this page helpful?