Adding Pages
A walkthrough of adding new HTML pages to ArchitectUI. Covers template creation, page registration, sidebar nav, per-page scripts, and reusing dashboard chrome.
The Two-Step Process
Every page in ArchitectUI is the combination of two things:
- A Handlebars template under
src/DemoPages/that defines the page content - An entry in
src/pages.jsthat registers the template withHtmlWebpackPluginso it gets compiled into an HTML file
Adding a page means creating one and registering the other. Webpack scans src/pages.js at startup, so you must restart the dev server after adding new entries — HMR doesn't track new pages.
Create the Template
Create a new .hbs file under src/DemoPages/ in the appropriate category. For example, a new analytics page:
// src/DemoPages/dashboards/my-dashboard.hbs
{{#> base }}
{{> AppSidebar/sidebar }}
<div class="app-main__outer">
<div class="app-main__inner">
{{> AppMain/page-title }}
<div class="row">
<div class="col-md-6">
<div class="main-card mb-3 card">
<div class="card-body">
<h5 class="card-title">Hello world</h5>
<p>My dashboard content goes here.</p>
</div>
</div>
</div>
</div>
{{> AppFooter/footer }}
</div>
</div>
{{/base}}
Three things to notice:
{{#> base }} ... {{/base}}wraps the content in the master layout (header, theme drawer, body classes). The block-partial syntax with#>lets the page contribute content to the master.{{> AppSidebar/sidebar }}includes the dashboard sidebar partial.{{> AppMain/page-title }}renders the page title bar usingtitle,description, andheading_iconvalues passed frompages.js.
Register the Page
Open src/pages.js and add a new entry to the array. The shape is:
{
output: "./my-dashboard.html", // relative URL of the output file
content: {
title: "My Dashboard", // page title (in <title> and page header)
description: "A custom dashboard example.", // meta description + page subtitle
heading_icon: "pe-7s-car icon-gradient bg-mean-fruit", // icon class for page header
},
template: "./src/DemoPages/dashboards/my-dashboard.hbs",
}
After saving, restart the dev server:
npm start
Visit http://localhost:8080/my-dashboard.html to view the rendered page.
Don't forget to restart. HMR doesn't track new entries in pages.js — the file is read once at webpack startup. Saving pages.js while the dev server runs has no effect.
Page Title Variables
The content object is interpolated into the template via HtmlWebpackPlugin. Within the template, refer to fields as htmlWebpackPlugin.options.[field]:
<title>{{ htmlWebpackPlugin.options.title }} - {{ htmlWebpackPlugin.options.description }}</title>
<meta name="description" content="{{ htmlWebpackPlugin.options.description }}">
The AppMain/page-title partial reads title, description, and heading_icon automatically.
Page Title Variants
ArchitectUI provides three page-title partials with different layouts:
| Partial | Description |
|---|---|
AppMain/page-title | Default — icon, title, description, with optional action buttons on the right |
AppMain/page-title-alt | Alternate layout with breadcrumb support |
AppMain/page-title-alt-2 | Compact variant with smaller icon |
AppMain/page-title-actions | Just the action button row (use alongside another title partial) |
Swap them by changing the {{> AppMain/... }} partial reference in your page template.
Add the Page to the Sidebar Nav
The dashboard sidebar is hard-coded in src/layout/AppSidebar/sidebar.hbs. To add your page to the nav, edit that file and add a list item. Example — adding "My Dashboard" under the Dashboards group:
<li>
<a href="my-dashboard.html">
<i class="metismenu-icon pe-7s-car"></i>
My Dashboard
</a>
</li>
For a nested sub-menu, use the MetisMenu pattern (a top-level <li> with a child <ul>):
<li>
<a href="#">
<i class="metismenu-icon pe-7s-rocket"></i>
My Section
<i class="metismenu-state-icon pe-7s-angle-down caret-left"></i>
</a>
<ul>
<li><a href="my-page-1.html">Page 1</a></li>
<li><a href="my-page-2.html">Page 2</a></li>
</ul>
</li>
Sidebar changes propagate to every page that uses {{> AppSidebar/sidebar }} — no other edits needed.
Per-Page JavaScript
Each Webpack entry in webpack.config.js produces a separate JS bundle. Pages only include the bundles they need.
Using an Existing Entry
If your page uses a feature that's already wired up (charts, datatables, form widgets), no JavaScript work is needed — the bundle is already built. Your page will pick it up via the default HtmlWebpackPlugin injection (which injects every entry into every page).
Adding a New Per-Page Feature
For a new feature, create an initializer file and register it as a Webpack entry:
// src/scripts-init/my-feature.js
import "./my-feature.scss"; // optional SCSS
document.addEventListener("DOMContentLoaded", function () {
const els = document.querySelectorAll("[data-my-feature]");
if (!els.length) return;
els.forEach(el => {
// initialize the feature on each element
});
});
// webpack.config.js
entry: {
// ...existing entries...
my_feature: "./src/scripts-init/my-feature.js",
}
Restart the dev server and the new entry is bundled. Use it on any page by adding the data-my-feature attribute to a target element.
The initializer pattern always starts with if (!els.length) return; — this guard means the bundle is safe to include on pages that don't use the feature. Webpack injects every entry into every page; the guard keeps the no-op path cheap.
Page Variants
Full-Screen Pages (Auth, Errors)
For pages that should not render the dashboard chrome (sidebar, header, drawer), extend basePages.hbs instead of base.hbs:
{{#> basePages }}
<div class="h-100 bg-plum-plate bg-animation">
<div class="d-flex h-100 justify-content-center align-items-center">
{{!-- your full-screen content here --}}
</div>
</div>
{{/basePages}}
Used by every auth page (login.hbs, register.hbs, etc.).
Documentation Pages
For docs-style pages with their own minimalist shell (top bar + left sidebar + content), extend docsBase.hbs:
{{#> docsBase docsActive="my-page"}}
<h1 class="docs-page-title">My Page</h1>
<section>
<h2>Section</h2>
<p>Content here.</p>
</section>
{{/docsBase}}
Add a matching nav entry in src/layout/AppDocs/sidebar.hbs so users can navigate to it.
Conventions and Tips
- Output paths are relative URLs.
"./my-page.html"emits to the root of the dist directory. For nested URLs (e.g."./admin/users.html") the build creates the subdirectory automatically. - Output names should match the URL pattern of nearby pages. Auth pages use
pages-*.html, components usecomponents-*.html, docs usedocs-*.html. This convention makes file finding easier. - Reuse
heading_iconclasses. The pattern is{icon-class} icon-gradient {bg-gradient-class}. Browse existing dashboards for examples. - Wrap page content in
{{!-- ...comments... --}}liberally — Handlebars comments are stripped from output and won't show up in HTML. - Don't put inline
<script>blocks in templates. Create a new entry insrc/scripts-init/instead. Inline scripts are harder to maintain and don't benefit from Webpack's module graph.