Handlebars Partials
A reference for ArchitectUI's partial system — how shared chrome (header, sidebar, footer, theme drawer) is composed, how the master base.hbs template wraps page content, and which partials are available out of the box.
How the Partial System Works
ArchitectUI uses handlebars-loader to compile .hbs templates at build time. Two directories are registered as partial roots:
// webpack.config.js
{
test: /\.hbs$/,
loader: "handlebars-loader",
options: {
helperDirs: [Path.join(__dirname, "src", "helpers")],
partialDirs: [
Path.join(__dirname, "src", "layout"),
Path.join(__dirname, "src", "DemoPages"),
],
},
}
Any .hbs file inside src/layout/ or src/DemoPages/ is referenceable as a partial. The partial's relative path from the root (minus the .hbs extension) becomes its name.
Examples:
| File path | Partial name |
|---|---|
src/layout/AppHeader/header.hbs | AppHeader/header |
src/layout/AppSidebar/sidebar.hbs | AppSidebar/sidebar |
src/layout/ThemeOptions/theme-options.hbs | ThemeOptions/theme-options |
src/layout/AppMain/page-title.hbs | AppMain/page-title |
src/layout/AppHeader/Components/header-right.hbs | AppHeader/Components/header-right |
Two Partial Syntaxes
Plain Inclusion: {{>}}
Drops the partial's content in place. Use this for anything you're not customizing per-page:
{{> AppSidebar/sidebar }}
{{> AppMain/page-title }}
{{> AppFooter/footer }}
Block Partial: {{#> ... }} ... {{/...}}
Wraps the partial around the content between the opening and closing tags. The wrapped content fills the partial's {{> @partial-block }} slot. This is how every ArchitectUI page extends base.hbs:
{{#> base }}
{{!-- this content fills base.hbs's @partial-block slot --}}
<h1>Hello</h1>
{{/base}}
Inside base.hbs, the slot looks like this:
<div class="app-main">
{{> @partial-block }}
</div>
That's the Handlebars equivalent of React's {children} or Vue's <slot />.
The Master Template: base.hbs
src/layout/base.hbs is the layout most pages extend. It includes the DOCTYPE, <head> with meta tags, the optional page loader, and the body shell with the header, theme drawer, and main content slot.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ htmlWebpackPlugin.options.title }} - {{ htmlWebpackPlugin.options.description }}</title>
{{!-- loader scripts and styles --}}
</head>
<body>
<div class="app-container app-theme-white body-tabs-shadow fixed-header fixed-sidebar">
{{> AppHeader/header }}
{{> ThemeOptions/theme-options }}
<div class="app-main MainAnimation-appear">
{{> @partial-block }}
</div>
</div>
{{> AppDrawer/drawer }}
</body>
</html>
Pages that extend base inherit the full app shell automatically. Their content fills only the .app-main region.
Alternative Layouts
Three top-level layouts ship with the template:
| Layout | Use for | Includes |
|---|---|---|
base |
Dashboards, components, forms, charts — anything inside the app | Header, theme drawer, app container, footer slot |
basePages |
Auth pages, error pages, full-screen marketing | Minimal — just a body with a slot, no header or sidebar |
docsBase |
Documentation pages (this site) | Standalone top bar + docs sidebar + content area |
Each layout reads htmlWebpackPlugin.options for title and description but otherwise renders independently of the others. A page can extend exactly one.
Available Layout Partials
Header
{{> AppHeader/header }} {{!-- full header --}}
{{> AppHeader/Components/logo }} {{!-- logo only --}}
{{> AppHeader/Components/header-left }} {{!-- search bar, mobile toggles --}}
{{> AppHeader/Components/header-right }} {{!-- dropdowns, user menu, drawer toggle --}}
Sidebar
{{> AppSidebar/sidebar }} {{!-- MetisMenu-powered sidebar nav --}}
Page Title Bar
{{> AppMain/page-title }} {{!-- icon + title + description + actions --}}
{{> AppMain/page-title-alt }} {{!-- with breadcrumbs --}}
{{> AppMain/page-title-alt-2 }} {{!-- compact variant --}}
{{> AppMain/page-title-actions }} {{!-- actions row only --}}
Footer
{{> AppFooter/footer }} {{!-- footer text + footer dots --}}
Theme Options Drawer
{{> ThemeOptions/theme-options }} {{!-- floating gear button + drawer panel --}}
Right-Side Drawer (Notifications)
{{> AppDrawer/drawer }} {{!-- slide-out drawer with notifications/tasks --}}
Documentation Sidebar
{{> AppDocs/sidebar }} {{!-- docs site sidebar with grouped nav --}}
Passing Context to a Partial
Partials accept context like Handlebars variables. Use the key=value syntax on the partial reference:
{{> AppDocs/sidebar docsActive="theming" }}
Inside the partial, docsActive is available as a regular variable. Combined with the eq helper in src/helpers/eq.js, this powers the docs sidebar's active-state highlighting:
<a class="docs-nav-link {{#if (eq docsActive 'theming')}}active{{/if}}"
href="docs-theming.html">
Theming
</a>
Handlebars Helpers
Helpers live in src/helpers/ and are registered automatically by the Handlebars loader's helperDirs config. The filename (minus .js) becomes the helper name.
The eq Helper
// src/helpers/eq.js
module.exports = function (a, b) {
return a === b;
};
Used in conditionals:
{{#if (eq docsActive 'theming')}}active{{/if}}
Adding Your Own Helper
Drop a new .js file in src/helpers/:
// src/helpers/uppercase.js
module.exports = function (str) {
if (typeof str !== "string") return "";
return str.toUpperCase();
};
Then use it in any template:
{{uppercase htmlWebpackPlugin.options.title}}
Restart the dev server after adding helpers — the loader caches helper registrations at startup.
Common Patterns
Reusing a Component Snippet Across Pages
Create a snippet under src/DemoPages/components/ or a new src/layout/Snippets/ directory, then reference it where needed:
{{!-- src/layout/Snippets/stat-card.hbs --}}
<div class="card mb-3 widget-content">
<div class="widget-content-wrapper">
<div class="widget-content-left">
<div class="widget-heading">Total Orders</div>
<div class="widget-numbers">1,847</div>
</div>
</div>
</div>
{{> Snippets/stat-card }}
Conditional Rendering Based on Page Title
{{#if (eq htmlWebpackPlugin.options.title "Analytics Dashboard")}}
<div class="alert alert-info">Welcome to Analytics</div>
{{/if}}
Pitfalls
- Trailing slashes in partial names. Use
{{> AppHeader/header }}, not{{> AppHeader/header/ }}or{{> /AppHeader/header }}. - Editing a partial does not re-emit every page. The dev server's HMR handles partial edits correctly, but if you see stale output, restart with
npm start. - Block partials need a closing tag.
{{#> base }}...{{/base}}— not{{#> base }}...{{/}}and not{{#> base }}...</base>. - Helpers must return a value. A helper that does
console.log()with no return statement renders as the literal string "undefined".