ArchitectUI Docs
Live Demo

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 pathPartial name
src/layout/AppHeader/header.hbsAppHeader/header
src/layout/AppSidebar/sidebar.hbsAppSidebar/sidebar
src/layout/ThemeOptions/theme-options.hbsThemeOptions/theme-options
src/layout/AppMain/page-title.hbsAppMain/page-title
src/layout/AppHeader/Components/header-right.hbsAppHeader/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:

LayoutUse forIncludes
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".

Next Steps

See Adding Pages to wire partials into a new page, or jump to Components to browse the ready-made widgets.