or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

express-integration.mdhelper-management.mdindex.mdpartial-management.md
tile.json

partial-management.mddocs/

Partial Management

Register and manage Handlebars partial templates, including bulk registration from directories with custom naming transformations.

Capabilities

Single Partial Registration

Register individual partial templates by name and content.

/**
 * Register single partial template
 * @param name - Partial name to use in templates
 * @param template - Template string content
 */
function registerPartial(name, template);

Usage Examples:

const hbs = require('hbs');

// Simple partial
hbs.registerPartial('header', '<header><h1>{{title}}</h1></header>');

// Partial with variables
hbs.registerPartial('user_card', `
  <div class="user-card">
    <h3>{{name}}</h3>
    <p>{{email}}</p>
    <span class="role">{{role}}</span>
  </div>
`);

// Partial using other partials
hbs.registerPartial('page_layout', `
  {{> header}}
  <main>{{> content}}</main>
  {{> footer}}
`);

In Templates:

<!-- Use registered partials -->
{{> header title="Welcome"}}

{{> user_card name="Alice" email="alice@example.com" role="Admin"}}

<!-- Partials inherit current context -->
{{#each users}}
  {{> user_card}}
{{/each}}

Directory-Based Partial Registration

Automatically register all partial templates from a directory with optional naming customization.

/**
 * Register all partials from directory
 * @param directory - Path to directory containing partial files
 * @param options - Optional configuration object
 * @param callback - Optional completion callback
 */
function registerPartials(directory, options, callback);

Basic Usage:

const hbs = require('hbs');
const path = require('path');

// Register all partials from directory
hbs.registerPartials(path.join(__dirname, 'views/partials'));

// With callback
hbs.registerPartials(path.join(__dirname, 'views/partials'), (err) => {
  if (err) {
    console.error('Failed to register partials:', err);
  } else {
    console.log('All partials registered successfully');
  }
});

Directory Structure Example:

views/partials/
├── header.hbs          → {{> header}}
├── footer.hbs          → {{> footer}}
├── user-card.html      → {{> user_card}}
├── nav menu.hbs        → {{> nav_menu}}
├── social/
│   ├── twitter.hbs     → {{> social/twitter}}
│   └── facebook.hbs    → {{> social/facebook}}
└── forms/
    ├── login-form.hbs  → {{> forms/login_form}}
    └── contact.hbs     → {{> forms/contact}}

Custom Naming Options

Customize how filenames are transformed into partial names using the

rename
option.

/**
 * Partial registration options
 * @typedef {Object} PartialOptions
 * @property {Function} rename - Function to transform filename to partial name
 */

/**
 * Register partials with custom naming
 * @param directory - Path to directory containing partial files
 * @param options - Configuration with rename function
 * @param callback - Optional completion callback
 */
function registerPartials(directory, options, callback);

Usage Examples:

const hbs = require('hbs');

// Default naming (spaces and hyphens become underscores)
hbs.registerPartials('./partials');
// user-profile.hbs → user_profile
// contact form.hbs → contact_form

// Custom naming: camelCase
hbs.registerPartials('./partials', {
  rename: function(name) {
    return name.replace(/[\s-_]+(.)/g, (match, letter) => letter.toUpperCase());
  }
});
// user-profile.hbs → userProfile
// contact form.hbs → contactForm

// Custom naming: preserve hyphens, remove spaces
hbs.registerPartials('./partials', {
  rename: function(name) {
    return name.replace(/\s+/g, '');
  }
});
// user-profile.hbs → user-profile
// contact form.hbs → contactform

// Custom naming: uppercase with prefix
hbs.registerPartials('./partials', {
  rename: function(name) {
    return 'PARTIAL_' + name.toUpperCase().replace(/\W/g, '_');
  }
});
// user-profile.hbs → PARTIAL_USER_PROFILE
// contact form.hbs → PARTIAL_CONTACT_FORM

Partial File Processing

Understanding how files are discovered and processed during directory registration.

File Discovery Rules:

  • Recursively walks all subdirectories
  • Only processes files with
    .html
    or
    .hbs
    extensions
  • Skips all other file types silently
  • Maintains directory structure in partial names

Naming Transformation Process:

  1. Get relative path from registered directory
  2. Remove file extension
  3. Convert backslashes to forward slashes (Windows compatibility)
  4. Apply custom
    rename
    function if provided
  5. Replace spaces with underscores as final step

Example Transformation:

Directory: /app/views/partials
File: /app/views/partials/admin/user-form.hbs

1. Relative path: admin/user-form.hbs
2. Remove extension: admin/user-form
3. Convert slashes: admin/user-form
4. Custom rename: admin/userForm (if camelCase rename)
5. Final cleanup: admin/userForm

Partial name: {{> admin/userForm}}

Asynchronous Registration

Partial registration is asynchronous and non-blocking, but templates wait for completion before rendering.

const hbs = require('hbs');

// Multiple registration calls are queued
hbs.registerPartials('./partials/global');
hbs.registerPartials('./partials/admin');
hbs.registerPartials('./partials/components');

// All registrations complete before template rendering
app.get('/', (req, res) => {
  // This render will wait for all partials to be registered
  res.render('index');
});

Registration Queue:

  • Multiple
    registerPartials
    calls are queued if called simultaneously
  • Queue prevents race conditions during directory walking
  • Templates automatically wait for queue completion
  • Callback functions execute after each individual registration

Error Handling

Partial registration handles various error conditions gracefully.

Error Scenarios:

  • Directory doesn't exist → Callback receives error
  • Permission denied → Callback receives error
  • File read errors → Individual files skipped, registration continues
  • Invalid template syntax → Individual files skipped, registration continues

Example Error Handling:

hbs.registerPartials('./nonexistent-directory', (err) => {
  if (err) {
    console.error('Partial registration failed:', err.message);
    // ENOENT: no such file or directory, open './nonexistent-directory'
  }
});

// Handle partial registration in application startup
function initializePartials() {
  return new Promise((resolve, reject) => {
    hbs.registerPartials('./views/partials', (err) => {
      if (err) {
        reject(err);
      } else {
        resolve();
      }
    });
  });
}

// Use in async startup
async function startApp() {
  try {
    await initializePartials();
    console.log('Partials loaded successfully');
    
    app.listen(3000, () => {
      console.log('Server ready');
    });
  } catch (err) {
    console.error('Failed to start app:', err);
    process.exit(1);
  }
}

Partial Context and Data

Partials inherit the current template context and can receive additional data.

Context Inheritance:

<!-- Template data: { user: { name: "Alice", role: "Admin" }, title: "Dashboard" } -->

{{> header}}
<!-- header partial has access to {{user.name}}, {{title}}, etc. -->

{{#with user}}
  {{> user_profile}}
  <!-- user_profile partial has access to {{name}}, {{role}} directly -->
{{/with}}

Passing Additional Data:

<!-- Pass additional variables to partials -->
{{> button_primary text="Click Me" size="large"}}

<!-- Combine context and additional data -->
{{> user_card role="Guest"}}
<!-- user_card gets both inherited context AND role="Guest" -->

Nested Partials

Partials can include other partials, creating reusable component hierarchies.

Example Nested Structure:

<!-- page_layout partial -->
<!DOCTYPE html>
<html>
  {{> head}}
  <body>
    {{> header}}
    <main>
      {{> content}}
    </main>
    {{> footer}}
  </body>
</html>

<!-- header partial -->
<header>
  {{> navigation}}
  {{> breadcrumbs}}
</header>

<!-- navigation partial -->
<nav>
  {{#each menuItems}}
    {{> nav_item}}
  {{/each}}
</nav>