Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

rvx-cli

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rvx-cli

Scaffold a production-ready Vite + React project with permissions, routing, Redux, and shadcn/ui

latest
npmnpm
Version
1.2.2
Version published
Maintainers
1
Created
Source

rvx-cli

A CLI that scaffolds production-ready Vite + React projects with a built-in permission engine, protected routing, centralized API layer, Redux state management, module generator, shadcn/ui components, and a pluggable theme system — all wired together and ready to go.

npx rvx-cli my-app
cd my-app
npm run dev

v1.2.0 ships with two themes:

  • Default — neutral monochrome shadcn palette (unchanged from v1.0.x)
  • Atlas — full enterprise design system: 29 components (KPI cards, charts, command palette, toast, modal, stepper, accent picker), 18 element showcase pages, custom dashboard + auth, 6 swappable accent palettes (maroon, red, green, blue, purple, teal) × 2 styles (gradient / solid)

Table of Contents

Why rvx-cli?

Most React scaffolding tools give you a blank canvas. rvx-cli gives you an opinionated, enterprise-ready architecture out of the box:

  • Permission enginecan(), canAny(), canAll() with super admin bypass, baked into routes, sidebar, and buttons
  • Theme system — pluggable, overlay-based. Pick a theme at scaffold time. Drop in a new themes/<name>/ folder and the CLI auto-discovers it. Atlas ships with 29 components + 18 demo pages
  • Module generatornpm run hcorp:add product creates page, add page, service, slice, API endpoints, permissions, route, and sidebar entry in one command. Branches on theme — Atlas modules use ListPage + StatusPill + FormSection
  • Feature-selectable — deselect Tailwind, shadcn, Redux, or Router during setup and the CLI cleanly strips all related code, files, and packages
  • POST-only API layer — centralized endpoints with React Query hooks, Bearer token auth, automatic cache invalidation
  • Responsive layout — sidebar + top-nav with mobile overlay, auto-close on navigation
  • Zero config — everything is wired: providers, store, routes, permissions, menu, theme — just start building modules

Quick Start

# Create a new project
npx rvx-cli my-app

# Or pass the name directly
npx rvx-cli my-app

The CLI will:

  • Prompt for a project name (or accept it as an argument)
  • Let you select which features to include via interactive checkboxes
  • Copy the template, strip deselected features, and adjust all code accordingly
  • Run npm install automatically
  • Output next steps
cd my-app
npm run dev       # Start dev server (default: http://localhost:5173)
npm run build     # Production build
npm run preview   # Preview production build

Feature Selection

During scaffolding, you choose which features to include:

? Select features:
  ◉ Tailwind CSS
  ◉ shadcn/ui
  ◉ Redux Toolkit
  ◉ React Router

? Choose theme:
❯ Default                        · Neutral monochrome shadcn palette
  Atlas — Enterprise Fintech     · Maroon gradient, slate neutrals, Inter typography, full component library
FeatureDefaultWhat happens when disabled
Tailwind CSSOnRemoves Tailwind packages, generates plain CSS reset, strips className attributes
shadcn/uiOnRemoves Radix/lucide packages, replaces <Button> with plain <button>, removes components/ui/, lib/, hooks/alert/. Auto-enables Tailwind if shadcn is selected
Redux ToolkitOnRemoves Redux packages, deletes src/features/ directory, module generator skips slice creation
React RouterOnRemoves router package, deletes src/layout/, generates state-based App.jsx with inline navigation

The module generator (hcorp:add) reads hcorp.config.json (tailwind, shadcn, redux, router, theme) and adapts generated code accordingly.

Theme System

Themes are pluggable overlays. Pick one at scaffold time. Each theme defines tokens, components, layout, pages, and head-injection scripts. The CLI auto-discovers themes from template/themes/<name>/meta.json — drop in a new folder and it appears in the prompt.

Theme directory layout

template/themes/<theme>/
├── meta.json                # { id, name, description, version }
├── tokens.css               # Tailwind v4 @theme tokens — overrides src/index.css
├── head.html                # Optional — injected before </head> in index.html (fonts, init scripts)
├── components/              # Optional — overlays src/components/<theme>/
├── layout/                  # Optional — overlays src/layout/ (sidebar, top-nav, layout, custom-routes, menu.item)
├── pages/                   # Optional — overlays src/pages/ (dashboard, auth, elements showcase)
├── constants/               # Optional — overlays src/constants/ (menu.data.js etc)
└── lib/                     # Optional — overlays src/lib/ (helpers like accent.js)

What CLI does at scaffold time

  • Copies template/ → target dir
  • Writes hcorp.config.json with chosen feature flags + theme: "<chosen>"
  • If theme ≠ default and Tailwind is enabled:
    • Copies themes/<theme>/tokens.csssrc/index.css
    • Overlays components/, layout/, pages/, constants/, lib/
    • Injects head.html snippet into index.html before </head>
  • Strips themes/ directory from final scaffold (only chosen theme materialized)

Adding a new theme

  • Create template/themes/<your-theme>/
  • Add meta.json:
    { "id": "your-theme", "name": "Your Theme", "description": "...", "version": "0.1.0" }
    
  • Add tokens.css with Tailwind v4 @theme { ... } block
  • (Optional) Add components/, layout/, pages/, etc.
  • CLI auto-detects on next run

hcorp.config.json — extended shape

{
  "tailwind": true,
  "shadcn": true,
  "redux": true,
  "router": true,
  "theme": "atlas"
}

The module generator branches on theme to scaffold theme-aware module code.

Atlas Theme

A full enterprise design system inspired by Hindalco V2 — premium B2B fintech aesthetic.

What it ships

  • Layout — Grid-based, sticky sidebar with collapse-to-icon mode + fly-out tooltips, blurred 60px topbar with breadcrumb / ⌘K search / notifications / help / profile pill
  • Dashboard — Featured KPI + 4 standard KPIs, line+bar chart, alerts list, top customers tile-list, activity timeline
  • Auth page — Split-panel design: gradient brand panel (logo + headline + stats) + form panel with show-password toggle, remember-me, animated submit
  • Element showcase/elements route with 18 demo pages, each with live preview + JSX code snippet + props table + extension notes
  • ⌘K command palette — global keyboard shortcut, items wired in layout.jsx for navigation
  • Toast system — bottom-right stack, 4 tones (success/warn/danger/info), auto-dismiss + manual close, useToast() hook
  • Tweaks fab — floating bottom-right button opens accent picker + density toggle
  • Density toggle[data-density="compact"] reduces row heights and gaps, persisted in localStorage
  • Inter + JetBrains Mono — loaded via Google Fonts preconnect (no FOUC)

Atlas color tokens

Tailwind utilities exposed by the active palette:

ClassCSS varUsed for
bg-primary / text-primary--color-primaryPrimary buttons, sidebar active, focus elements
bg-accent / text-accent--color-accentBrighter accent — chips, highlights
bg-atlas-1 / bg-atlas-2 / bg-atlas-deep--color-atlas-*Brand tones (gradient stops)
bg-atlas-soft / bg-atlas-soft-2--color-atlas-soft*10% / 6% brand tints
bg-atlas-{success,warn,danger,info}--color-atlas-*Semantic tones (constant across palettes)
bg-atlas-{success,warn,danger,info}-soft--color-atlas-*-softSemantic tints
bg-atlas-surface-2 / surface-3--color-atlas-surface-*Subtle layered backgrounds
text-atlas-{fg,muted,subtle,faint}--color-atlas-*Text scale
var(--atlas-gradient)inline style135° brand gradient (or solid in solid mode)
shadow-atlas-{sm,md,lg}presetThree shadow elevations

Atlas Components

29 reusable components in src/components/atlas/. All exported via barrel import { ... } from "@/components/atlas".

Layout & content

ComponentPurpose
Card, CardHead, CardBody, CardFootSurface container with optional head/body/foot
PageHeaderTop-of-page header — eyebrow, title, subtitle, actions
ViewHeaderDetail-page header with inline meta row
KvGridRead-only field grid (1–4 cols) — { label, value, mono?, muted? }[]
EmptyStatePlaceholder when no records — icon + title + description + action

Data display

ComponentPurpose
DataTableSticky-header table with custom render + alignment + row click
ListPageDataTable + chip filters + search input shell
TileListVertical list with title, subtitle, value, optional progress bar
TimelineVertical timeline with connector + dot markers

Status & metrics

ComponentPurpose
StatusPillAuto-toned status badge — pass any business status
BadgeGeneric counts/labels — 8 variants × 3 sizes, optional dot/icon
KPIHeadline metric tile — label, value, delta pill, sparkline. Featured variant uses gradient
SparklineInline mini-chart (pure SVG)
LineBarChartComposite line + bar chart (pure SVG, no deps)

Form primitives

ComponentPurpose
InputText/email/password/etc — 38px height, focus glow, optional icon, error state
SelectNative select with custom chevron arrow
TextareaResizable textarea matching Input style
ToggleSwitch — checked uses gradient, sm/md/lg sizes
CheckboxCustom checkbox — checked uses gradient
Tabs, TabList, Tab, TabPanelUnderline-style tabs with optional badges
StepperMulti-step wizard progress (numbered circles + connectors)
FormSection, Field, FormGridCard shell + label/hint/error wrapper + responsive grid

Overlays

ComponentPurpose
ModalCentered dialog with backdrop blur — sm/md/lg/xl sizes, esc + click-outside
ToastProvider + useToast()Bottom-right toast stack — 4 tones, auto-dismiss, persistent option
Dropdown, DropdownItem, DropdownDivider, DropdownLabelClick-outside menu
CommandPaletteProvider + useCommandPalette()⌘K global search + actions
TweaksFabFloating bottom-right gear — opens accent picker + density toggle

Theme controls

ComponentPurpose
AccentPickerReusable swatch picker — palette + style toggle, persists to localStorage

Element showcase

Atlas ships with /elements route — an interactive component catalog. Each tile links to /elements/<name> showing live preview + JSX code snippet + props table + usage notes. 18 pages total (Colors, PageHeader, ViewHeader, StatusPill, Badge, EmptyState, Table, ListPage, KPI, Chart, FormControls, Tabs, Stepper, Modal, Toast, Dropdown, CommandPalette + index).

Color Palettes & Live Switcher

Atlas ships with 6 accent palettes × 2 styles. Switch live via the floating tweaks fab (bottom-right) or programmatically via setAccent() from @/lib/accent.

Palettes

IDNameFrom → To
maroonMaroon (default)#a34d51#8a1a1f
redRed#ef4444#b91c1c
greenGreen#22c55e#15803d
blueBlue#3b82f6#1d4ed8
purplePurple#a855f7#7e22ce
tealTeal#14b8a6#0f766e

Styles

  • gradient — 135° linear-gradient from light to dark stop (default)
  • solid — flat dark stop only

How it works

<html data-accent="maroon" data-accent-style="gradient">

Each palette block in themes/atlas/tokens.css redefines --color-primary, --color-accent, --color-ring, --color-atlas-1/2/deep/soft/soft-2, --color-sidebar-primary/ring, --atlas-gradient, --atlas-shadow-glow. Tailwind utilities (bg-primary, text-atlas-2, etc.) auto-update at runtime — no rebuild.

Persistence

Inline init script in index.html reads localStorage.atlas-accent + atlas-accent-style and sets the data-attrs before React renders — no flash of unstyled content.

Adding a new palette

  • Add a [data-accent="<name>"] { ... } block in tokens.css
  • Add an entry to ACCENTS in lib/accent.js:
    { id: "orange", name: "Orange", from: "#fb923c", to: "#c2410c" }
    
  • Picker auto-updates

Programmatic API

import { setAccent, setAccentStyle, getAccent, ACCENTS } from "@/lib/accent";

setAccent("blue");           // switch palette
setAccentStyle("solid");     // switch style
getAccent();                 // → "blue"

Project Structure

src/
├── components/ui/                    # shadcn/ui components (Button, AlertDialog, Switch)
├── constants/
│   ├── api/
│   │   └── api.js                    # Centralized API endpoints with crud() helper
│   ├── config/
│   │   ├── permissions.js            # Permission constants per module
│   │   ├── colors.js                 # Color palette for JS usage (charts, inline styles)
│   │   └── text.js                   # UI text/label constants
│   └── data/
│       └── menu.data.js              # Sidebar menu structure with permission filtering
├── context/
│   ├── auth/auth.context.jsx         # Auth state + permission engine (can, canAny, canAll)
│   ├── theme/theme.context.jsx       # Light/dark theme toggle
│   └── mobile/mobile.context.jsx     # Mobile detection + sidebar state
├── features/
│   ├── store.js                      # Redux store configuration
│   └── master/
│       └── <module>/
│           └── <module>.slice.js     # Redux slice with CRUD reducers
├── hooks/
│   ├── alert/use-alert.jsx           # Alert dialog state management hook
│   └── api/use-api.jsx               # React Query hooks (useApiQuery, useApiMutation)
├── layout/
│   ├── layout.jsx                    # Main layout (sidebar + top-nav + Outlet)
│   ├── top-nav.jsx                   # Header with hamburger, user info, logout
│   ├── sidebar.jsx                   # Permission-filtered navigation sidebar
│   ├── custom-routes.jsx             # Route definitions with ProtectedRoute wrapper
│   └── menu.item.jsx                 # NavLink menu item with active state
├── lib/
│   └── utils.js                      # cn() utility for Tailwind class merging
├── pages/views/
│   ├── dashboard.jsx                 # Dashboard landing page
│   ├── auth/
│   │   ├── auth.jsx                  # Login page with form
│   │   └── auth.service.js           # Auth API hooks (login, logout, me)
│   ├── admin/
│   │   └── permissions.jsx           # Permission toggle admin page (live testing)
│   ├── master/
│   │   └── <module>/
│   │       ├── <module>.jsx          # Module list page
│   │       ├── <module>.service.js   # Module API hooks (React Query)
│   │       └── add/
│   │           └── add.<module>.jsx  # Module add/create page
│   ├── dev-guide/
│   │   ├── dev-guide.jsx             # Interactive developer documentation
│   │   └── sections.jsx              # All documentation sections
│   └── errors/
│       ├── not-found.jsx             # 404 page
│       ├── server-error.jsx          # 500 page
│       ├── unauthorized.jsx          # 403 page
│       └── countdown-redirect.jsx    # Auto-redirect with countdown timer
├── main.jsx                          # Entry point with all providers stacked
└── index.css                         # Tailwind v4 theme variables + base styles

Module Generator

The most powerful feature — generate a complete CRUD module with a single command:

npm run hcorp:add product

What it creates

FilePath
List pagesrc/pages/views/master/product/product.jsx
Add pagesrc/pages/views/master/product/add/add.product.jsx
Service filesrc/pages/views/master/product/product.service.js
Redux slicesrc/features/master/product/product.slice.js

What it updates automatically

FileChange
constants/api/api.jsAdds PRODUCT: crud("product") — generates LIST, CREATE, UPDATE, DELETE, SEARCH endpoints
constants/config/permissions.jsAdds PRODUCT: { VIEW, ADD, EDIT, DELETE } permission block
features/store.jsImports and registers productReducer
constants/data/menu.data.jsAdds "Product" to the Master menu group with product.view permission
layout/custom-routes.jsxAdds protected routes for /master/product and /master/product/add
context/auth/auth.context.jsxAdds product.view, product.button.add, product.button.edit, product.button.delete to default permissions

Config-aware generation

The generator reads hcorp.config.json and adapts:

  • No Redux — skips slice file and store registration
  • No Router — skips route registration and useNavigate
  • No shadcn — uses plain <button> instead of <Button>
  • No Tailwind — omits all className attributes

Permission System

Permission format

Every module follows a strict 4-permission pattern:

<module>.view              → View the module page
<module>.button.add        → Show add button / access add page
<module>.button.edit       → Show edit button
<module>.button.delete     → Show delete button

Permission constants

// src/constants/config/permissions.js
export const PERMISSIONS = {
  CUSTOMER: {
    VIEW: "customer.view",
    ADD: "customer.button.add",
    EDIT: "customer.button.edit",
    DELETE: "customer.button.delete",
  },
};

Three permission functions

import { useAuth } from "@/context/auth/auth.context";

const { can, canAny, canAll } = useAuth();

can("customer.view")                                    // single check
canAny(["customer.button.add", "customer.button.edit"]) // has ANY of these
canAll(["customer.view", "customer.button.delete"])      // has ALL of these

Super admin bypass

Roles super-admin, Super Admin, or superadmin (case-insensitive) automatically pass all permission checks.

Where permissions are enforced

LayerHow
Buttons{can(PERMISSIONS.CUSTOMER.ADD) && <Button>Add</Button>}
Menu itemspermission: "customer.view" in menu.data.js — sidebar auto-filters
Routes<ProtectedRoute permission="customer.view"> wrapper
Pagescan() / canAny() / canAll() in component logic

Admin permissions page

Navigate to /admin/permissions to toggle permissions in real-time with switch toggles. Shows all registered permissions with a live JSON view — useful for testing permission-based UI behavior during development.

API Layer

Endpoint definitions

// src/constants/api/api.js
const BASE_URL = import.meta.env.VITE_API_BASE_URL || "";

function crud(module) {
  return {
    LIST:   `${BASE_URL}/api/${module}/list`,
    CREATE: `${BASE_URL}/api/${module}/create`,
    UPDATE: `${BASE_URL}/api/${module}/update`,
    DELETE: `${BASE_URL}/api/${module}/delete`,
    SEARCH: `${BASE_URL}/api/${module}/search`,
  };
}

export const API = {
  AUTH: {
    LOGIN:  `${BASE_URL}/api/auth/login`,
    LOGOUT: `${BASE_URL}/api/auth/logout`,
    ME:     `${BASE_URL}/api/auth/me`,
  },
  CUSTOMER: crud("customer"),
};

Custom endpoints

export const API = {
  // ... existing
  REPORT: {
    SALES: `${BASE_URL}/api/report/sales`,
    INVENTORY: `${BASE_URL}/api/report/inventory`,
  },
};

The crud() helper generates 5 standard endpoints per module. The module generator auto-adds MODULE: crud("module") when you run hcorp:add.

Service Files & React Query

Each module has a service file that exports React Query hooks. The hooks connect API endpoints to the useApi hook.

useApiQuery — Fetching data

import { useApiQuery } from "@/hooks/api/use-api";
import { API } from "@/constants/api/api";

const { data, isLoading, error } = useApiQuery(
  ["customer", "list"],     // cache key
  API.CUSTOMER.LIST,        // endpoint URL
  { page: 1, limit: 10 }   // POST body (optional)
);
ParamTypeDescription
keystring | string[]React Query cache key
endpointstringFull API URL from api.js
bodyobjectPOST body (auto JSON.stringify)
optionsobjectReact Query options (enabled, staleTime, etc.)

useApiMutation — Create/Update/Delete

import { useApiMutation } from "@/hooks/api/use-api";
import { API } from "@/constants/api/api";

const { mutate, isPending } = useApiMutation(
  API.CUSTOMER.CREATE,
  {
    invalidateKeys: [["customer", "list"]],  // auto-refresh list after success
    onSuccess: (data) => { /* handle */ },
    onError: (error) => { /* handle */ },
  }
);

mutate({ name: "John", email: "john@example.com" });
ParamTypeDescription
endpointstringFull API URL from api.js
options.invalidateKeysstring[][]Cache keys to invalidate on success
options.onSuccessfunctionSuccess callback
options.onErrorfunctionError callback

What the hook handles automatically

  • All requests use POST method
  • Bearer token from localStorage.getItem("token")
  • Auto JSON.stringify of request body
  • Auto JSON parse of response
  • Error extraction from response body
  • Cache invalidation on mutation success
  • Loading / error / data states via React Query

Service file pattern

// src/pages/views/master/customer/customer.service.js
import { useApiQuery, useApiMutation } from "@/hooks/api/use-api";
import { API } from "@/constants/api/api";

export function useCustomerList(params) {
  return useApiQuery(["customer", "list", params], API.CUSTOMER.LIST, params);
}

export function useCustomerCreate(options = {}) {
  return useApiMutation(API.CUSTOMER.CREATE, {
    invalidateKeys: [["customer", "list"]],
    ...options,
  });
}

export function useCustomerUpdate(options = {}) {
  return useApiMutation(API.CUSTOMER.UPDATE, {
    invalidateKeys: [["customer", "list"]],
    ...options,
  });
}

export function useCustomerDelete(options = {}) {
  return useApiMutation(API.CUSTOMER.DELETE, {
    invalidateKeys: [["customer", "list"]],
    ...options,
  });
}

Redux Store

Redux Toolkit is used for client-side global state. Server/API state is handled by React Query.

Store configuration

// src/features/store.js
import { configureStore } from "@reduxjs/toolkit";
import customerReducer from "./master/customer/customer.slice";

export const store = configureStore({
  reducer: {
    customer: customerReducer,
  },
});

Slice pattern

// src/features/master/customer/customer.slice.js
import { createSlice } from "@reduxjs/toolkit";

const customerSlice = createSlice({
  name: "customer",
  initialState: { list: [], loading: false, error: null },
  reducers: {
    setCustomers: (state, action) => { state.list = action.payload; },
    addCustomer: (state, action) => { state.list.push(action.payload); },
    updateCustomer: (state, action) => {
      const index = state.list.findIndex(item => item.id === action.payload.id);
      if (index !== -1) state.list[index] = action.payload;
    },
    removeCustomer: (state, action) => {
      state.list = state.list.filter(item => item.id !== action.payload);
    },
    setLoading: (state, action) => { state.loading = action.payload; },
    setError: (state, action) => { state.error = action.payload; },
  },
});

export const { setCustomers, addCustomer, updateCustomer, removeCustomer, setLoading, setError } = customerSlice.actions;
export default customerSlice.reducer;

When to use what

Use CaseTechnology
API data (fetch, create, update, delete)React Query (useApiQuery / useApiMutation)
Client-only global state (UI state, selections)Redux Toolkit
Auth / Theme / Mobile stateContext API

Auth Context

// src/context/auth/auth.context.jsx
import { useAuth } from "@/context/auth/auth.context";

const {
  user,                    // { name, email, role, permissions } | null
  isAuthenticated,         // boolean
  can(permission),         // check single permission
  canAny([permissions]),   // check if user has ANY
  canAll([permissions]),   // check if user has ALL
  isSuperAdmin(),          // check super admin role
  login(userData),         // set user + save token to localStorage
  logout(),                // clear user + token
  updatePermissions([]),   // update permission array
  updateRole(role),        // update user role
} = useAuth();

Login flow

const { login } = useAuth();

login({
  name: "John",
  email: "john@example.com",
  role: "admin",
  permissions: ["customer.view", "customer.button.add"],
  token: "jwt-token-from-api",
});
// Token is stored in localStorage automatically

Default demo user

The template ships with a default super-admin user for development with all permissions pre-enabled. Replace this with your actual auth API integration.

Routing & Protected Routes

Route structure

/auth                    → Login page (public, redirects if authenticated)
/                        → Dashboard (authenticated)
/master/customer         → Customer list (permission: customer.view)
/master/customer/add     → Add customer (permission: customer.button.add)
/admin/permissions       → Permission admin (authenticated)
/dev-guide               → Developer guide (authenticated)
/error/unauthorized      → 403 page
/error/server-error      → 500 page
*                        → 404 page

ProtectedRoute wrapper

function ProtectedRoute({ children, permission }) {
  const { isAuthenticated, can } = useAuth();

  if (!isAuthenticated) return <Navigate to="/auth" replace />;
  if (permission && !can(permission)) return <Navigate to="/error/unauthorized" replace />;

  return children;
}

Adding a route manually

// In custom-routes.jsx
import Product from "@/pages/views/master/product/product";

<Route
  path="master/product"
  element={
    <ProtectedRoute permission="product.view">
      <Product />
    </ProtectedRoute>
  }
/>

Or use npm run hcorp:add product to do this automatically.

Layout System

┌────────────────────────────────────────────────┐
│                   TopNav                        │
│  [☰]           App Name           [User] [Out] │
├──────────┬─────────────────────────────────────┤
│          │                                     │
│ Sidebar  │           <Outlet />                │
│  (w-64)  │        (page content)               │
│          │                                     │
│  Menu    │                                     │
│  Items   │                                     │
│          │                                     │
└──────────┴─────────────────────────────────────┘
FilePurpose
layout/layout.jsxMain wrapper — renders Sidebar + TopNav + <Outlet /> for child routes
layout/top-nav.jsxHeader with hamburger toggle, user display, and logout button
layout/sidebar.jsxNavigation sidebar with permission-filtered menu items, grouped sections
layout/menu.item.jsxIndividual NavLink item with active state styling

The sidebar width is w-64 (256px). On desktop it pushes content via ml-64. On mobile it overlays with a backdrop.

Sidebar & Menu Configuration

// src/constants/data/menu.data.js
import { LayoutDashboard, Users, Shield } from "lucide-react";

export const MENU_ITEMS = [
  {
    title: "Dashboard",
    items: [
      { label: "Dashboard", path: "/", icon: LayoutDashboard, permission: null },
    ],
  },
  {
    title: "Master",
    items: [
      { label: "Customer", path: "/master/customer", icon: Users, permission: "customer.view" },
    ],
  },
  {
    title: "Admin",
    items: [
      { label: "Permissions", path: "/admin/permissions", icon: Shield, permission: null },
    ],
  },
];
  • Set permission: null to make an item always visible to authenticated users
  • Set permission: "module.view" to filter by permission — the sidebar auto-hides items the user can't access
  • Icons are from lucide-react
  • The module generator automatically adds entries to the Master group

Theme & Styling

Tailwind CSS v4 theme variables

All theme colors are defined as CSS variables in src/index.css:

@theme {
  --color-background: #ffffff;
  --color-foreground: #0a0a0a;
  --color-primary: #171717;
  --color-primary-foreground: #fafafa;
  --color-secondary: #f5f5f5;
  --color-muted: #f5f5f5;
  --color-muted-foreground: #737373;
  --color-accent: #f5f5f5;
  --color-destructive: #ef4444;
  --color-border: #e5e5e5;
  --color-sidebar-background: #fafafa;
  --color-sidebar-foreground: #404040;
  --radius-sm: 0.25rem;
  --radius-md: 0.375rem;
  --radius-lg: 0.5rem;
}

Using theme colors

<div className="bg-primary text-primary-foreground">Primary</div>
<div className="bg-destructive">Error</div>
<div className="text-muted-foreground">Subtle text</div>
<div className="bg-sidebar-background">Sidebar</div>

Theme toggle (light/dark)

import { useTheme } from "@/context/theme/theme.context";

const { theme, toggleTheme } = useTheme();
// theme is "light" or "dark"

Color constants for JavaScript

// For charts, inline styles, etc.
import { COLORS } from "@/constants/config/colors";
// COLORS.primary, COLORS.danger, COLORS.success, etc.

To change the entire app's color scheme, edit the CSS variables in index.css — all components reference these variables.

Mobile & Responsive

import { useMobile } from "@/context/mobile/mobile.context";

const { isMobile, sidebarOpen, setSidebarOpen, toggleSidebar } = useMobile();
PropertyTypeDescription
isMobilebooleantrue when viewport < 768px
sidebarOpenbooleanCurrent sidebar state
setSidebarOpenfunctionSet sidebar state directly
toggleSidebarfunctionToggle sidebar open/close

Behavior:

  • Desktop: sidebar starts open, pushes main content
  • Mobile: sidebar starts closed, opens as overlay with backdrop
  • Auto-closes on mobile when a menu item is clicked
  • Listens to window resize events

Alert Dialog System

Use the useAlert hook instead of window.alert() or window.confirm():

import { useAlert } from "@/hooks/alert/use-alert";
import {
  AlertDialog, AlertDialogContent, AlertDialogHeader,
  AlertDialogTitle, AlertDialogDescription,
  AlertDialogFooter, AlertDialogAction, AlertDialogCancel,
} from "@/components/ui/alert-dialog";

function MyComponent() {
  const { alertState, showAlert, handleConfirm, handleCancel } = useAlert();

  const handleDelete = () => {
    showAlert({
      title: "Delete Customer",
      description: "Are you sure? This cannot be undone.",
      onConfirm: () => { /* delete logic */ },
    });
  };

  return (
    <>
      <Button onClick={handleDelete}>Delete</Button>

      <AlertDialog open={alertState.open}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>{alertState.title}</AlertDialogTitle>
            <AlertDialogDescription>{alertState.description}</AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel onClick={handleCancel}>Cancel</AlertDialogCancel>
            <AlertDialogAction onClick={handleConfirm}>Confirm</AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </>
  );
}
MethodDescription
showAlert({ title, description, onConfirm, onCancel })Open the dialog
handleConfirm()Execute onConfirm and close
handleCancel()Execute onCancel and close
closeAlert()Close without running callbacks
alertState.openBoolean — is dialog currently open

shadcn/ui Components

Pre-installed components in src/components/ui/:

ComponentFileRadix Primitive
Buttonbutton.jsx@radix-ui/react-slot
AlertDialogalert-dialog.jsx@radix-ui/react-alert-dialog
Switchswitch.jsx@radix-ui/react-switch

Button variants

import { Button } from "@/components/ui/button";

<Button>Default</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline">Cancel</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button size="icon"><Icon /></Button>

Adding more shadcn components

Since this is JavaScript (not TypeScript), npx shadcn@latest add won't work directly. Instead:

  • Copy the component source from shadcn/ui docs
  • Create the file in src/components/ui/
  • Remove TypeScript types
  • Install the required Radix primitive: npm install @radix-ui/react-*

Environment Variables

All env vars must be prefixed with VITE_ to be exposed to client code.

FileWhen LoadedGit Tracked
.envAlways (base defaults)Yes
.env.localAlways, overrides .envNo
.env.stagingWhen --mode stagingYes
.env.productionWhen building for prodYes

Default variables

VITE_API_BASE_URL=http://localhost:3000
VITE_APP_NAME=HCorp App
VITE_PORT=5173

Accessing in code

const baseUrl = import.meta.env.VITE_API_BASE_URL;
const appName = import.meta.env.VITE_APP_NAME;

VITE_API_BASE_URL is used inside constants/api/api.js to construct all endpoint URLs — you never need to reference it directly elsewhere.

Staging build

npx vite build --mode staging

Configuration File

After scaffolding, hcorp.config.json is created in the project root:

{
  "tailwind": true,
  "shadcn": true,
  "redux": true,
  "router": true
}

This file tells the module generator which features are available. Do not manually change it after project creation — it reflects what was installed during scaffolding.

Naming Conventions

File names

TypePatternExample
Page<module>.jsxcustomer.jsx
Add Pageadd.<module>.jsxadd.customer.jsx
Service<module>.service.jscustomer.service.js
Slice<module>.slice.jscustomer.slice.js
Context<name>.context.jsxauth.context.jsx
Hookuse-<name>.jsxuse-api.jsx
Data<name>.data.jsmenu.data.js

Service hook names

use<Module>List     → useCustomerList
use<Module>Create   → useCustomerCreate
use<Module>Update   → useCustomerUpdate
use<Module>Delete   → useCustomerDelete

Redux slice actions

set<Module>s    → setCustomers
add<Module>     → addCustomer
update<Module>  → updateCustomer
remove<Module>  → removeCustomer

Permission names

<module>.view              → customer.view
<module>.button.add        → customer.button.add
<module>.button.edit       → customer.button.edit
<module>.button.delete     → customer.button.delete

Folder structure per module

src/
├── constants/api/api.js             → API.CUSTOMER: crud("customer")
├── constants/config/permissions.js  → PERMISSIONS.CUSTOMER
├── features/master/customer/        → customer.slice.js
└── pages/views/master/customer/
    ├── customer.jsx                 → List page
    ├── customer.service.js          → React Query hooks
    └── add/
        └── add.customer.jsx         → Add page

Built-in Dev Guide

Every scaffolded project includes an interactive developer guide at /dev-guide. It covers:

  • Overview & tech stack
  • Installation & setup
  • Project structure & naming conventions
  • Environment configuration
  • API layer & endpoint definitions
  • useApiQuery & useApiMutation hook reference
  • Service file patterns
  • Redux store & slice patterns
  • Auth context & login flow
  • Permission system (format, constants, functions, UI usage)
  • Protected routes
  • Layout system
  • Sidebar & menu configuration
  • shadcn/ui components
  • Alert hook
  • Module generator (hcorp:add) with generated file examples
  • Theme & Tailwind configuration
  • hcorp.config.json reference
  • Vite configuration

The guide is built as a React page with sidebar navigation — it's a living reference that stays in sync with the template.

Error Pages

PageRouteDescription
Not Found/error/not-found or *404 page with countdown redirect to home
Server Error/error/server-error500 page with countdown redirect
Unauthorized/error/unauthorized403 page with countdown redirect

All error pages use the CountdownRedirect component that displays a countdown timer and auto-redirects to the home page.

Tech Stack

TechnologyVersionPurpose
Vite6.0Build tool & dev server
React18.3UI library (JavaScript only, no TypeScript)
Tailwind CSS4.1Utility-first styling via Vite plugin (@theme tokens, runtime data-attr palette swap)
shadcn/uiAccessible UI components (Radix + Tailwind)
Redux Toolkit2.5Client-side global state management
React Router7.1Client-side routing with protected routes
TanStack React Query5.62Server state, caching, API call management
Lucide React0.468Icon library
Radix UIAccessible primitives (AlertDialog, Switch, Slot)
Inter + JetBrains MonolatestAtlas typography (loaded via Google Fonts preconnect)

What's New in 1.2.0

  • Theme system — pluggable, overlay-based, auto-discovered from template/themes/<name>/meta.json
  • Atlas theme — 29 components, 18 element showcase pages, custom dashboard + auth, full Hindalco V2 design language (maroon gradient, 60px header, Inter typography)
  • 6 accent palettes × 2 styles — switchable at runtime via TweaksFab (bottom-right floating button), persists to localStorage
  • ⌘K command palette — global keyboard search wired to topbar
  • Toast / Modal / Dropdown — overlay system with useToast() and useCommandPalette() hooks
  • Density toggle[data-density="compact"] reduces row heights and gaps
  • Module generator branches on theme — Atlas modules use ListPage + StatusPill + FormSection
  • Default theme remains byte-identical to v1.0.x output when chosen

See CHANGELOG.md for full version history.

License

MIT

Keywords

vite

FAQs

Package last updated on 11 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts