Adding a Page
Add a new page to Gentelella in two steps. Drop an HTML file in production/ — Vite auto-discovers it — then add one entry to the NAV array so it shows up in the sidebar and ⌘K command palette.
Last updated May 22, 2026
The Vite multi-page setup is designed so that adding a page is two small edits. Vite auto-discovers every production/*.html file as a Rollup entry — no manual config — and the shell renders from a single NAV array.
Let’s add a hypothetical “Reports” page.
1. Create the HTML file
Drop a new file in production/. The fastest way is to copy production/plain_page.html — it’s the canonical blank starter — then change the title and body attributes:
<!-- production/reports.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reports | Gentelella 2026 v4</title>
<link rel="icon" href="../images/favicon.svg" type="image/svg+xml">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script type="module" src="/src/main-v4.js"></script>
</head>
<body data-shell="admin" data-page="reports" data-breadcrumb="Home > Reports">
<main class="main">
<div class="page-wrapper">
<div class="page-header">
<div class="page-header-row">
<div>
<div class="pretitle">Q2 2026</div>
<h1 class="page-title">Reports</h1>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="card-title">Your content here</h3>
</div>
<div class="card-body">
<!-- your page content -->
</div>
</div>
</div>
</main>
</body>
</html>
What the body attributes do
data-shell="admin"— opt-in. Gets you the sidebar, topbar, and footer injected at build time byvite.config.js. Skip it for auth or marketing pages that want a different layout.data-page="reports"— must match thekeyyou’ll add toNAVin step 2. The sidebar uses this to highlight the active item.data-breadcrumb="Home > Reports"—>-separated string. Last segment becomes the current page label.
Note that pages live in production/, not src/. That’s a quirk from the legacy Gentelella layout, kept for backwards compatibility — assets in src/ get bundled by Vite; HTML in production/ is the entry-point root.
2. Add the nav entry
Open src/v4/shell-render.js and find the NAV array. Add a new item to whichever section makes sense — let’s say the General group:
export const NAV = [
{
label: 'General',
items: [
{
text: 'Dashboards', icon: 'dashboard',
children: [
{ key: 'dashboard', href: 'index.html', text: 'Operations' },
// ...
]
},
// Add this:
{ key: 'reports', href: 'reports.html', text: 'Reports', icon: 'pages' },
// ...
]
},
// ...
];
The required fields are key (matches your page’s data-page), href (the HTML file in production/), text (sidebar label), and icon (a string key into the ICONS map further down in shell-render.js).
Available icons
The ICONS object near the bottom of shell-render.js is a name → SVG path map. Common ones: dashboard, forms, tables, charts, calendar, map, chat, mail, kanban, files, shop, tag, cart, users, profile, settings, pages, layout, code, paint, type, icons, media, help, bell.
To add a custom icon: append a new entry to the ICONS map with the inner <path> markup. Lucide and Tabler icons work well — 24×24 viewBox, stroked, fill: none.
Optional fields
badge: { text: 'New', cls: 'badge-teal' }— pill next to the label. Three badge classes are defined in_layout.scss:badge-teal,badge-blue,badge-red. Add more by extending the.badgerule with new color modifiers if you need them.children: [...]— turns the item into a parent group with a submenu. Children are flat{ key, href, text }objects without their own icons. The parent stays expanded while any child is the active page.
{
text: 'Reports', icon: 'pages',
children: [
{ key: 'reports-sales', href: 'reports_sales.html', text: 'Sales' },
{ key: 'reports-traffic', href: 'reports_traffic.html', text: 'Traffic' }
]
},
3. Restart Vite
Vite scans production/ for entry HTML files only at startup. Stop npm run dev (Ctrl+C) and run it again:
npm run dev
Visit http://localhost:9173/production/reports.html — the page renders with the sidebar, topbar, footer, theme toggle, dark mode, and ⌘K palette all wired up. The Reports nav item is highlighted, and the topbar shows “Home > Reports”.
What you didn’t have to do
- No router config — pages are static HTML, served by Vite as separate entries
- No manual
rollupOptions.inputregistration —discoverEntries()invite.config.jsreadsproduction/and registers every.htmlautomatically - No shell template duplication — the build-time plugin injects the sidebar/topbar/footer for any page with
data-shell="admin" - No CSS imports —
main-v4.jsimportsmain.scss, every page links to the same compiled bundle
Removing a page
Just the reverse:
- Delete
production/reports.html - Remove the entry from
NAVinshell-render.js - Restart the dev server (so Vite rescans entries)
Common gotchas
Sidebar/topbar/footer missing. You forgot data-shell="admin" on <body>. Without it, the Vite plugin skips shell injection.
Sidebar renders but no item is highlighted. Your data-page value doesn’t match any key in NAV. Both have to agree exactly — strings are case-sensitive.
Breadcrumbs are blank. Forgot data-breadcrumb, or used the wrong separator. The separator is > with spaces around it: "Home > Section > Page".
Page renders without styles. Make sure <script type="module" src="/src/main-v4.js"></script> is in <head>. That’s the entry that imports main.scss.
Vite says “404 — page not found”. You added the HTML but didn’t restart the dev server. Vite reads the entry list only at startup.