Salesforce • 5 min read

Part 6: Building Modern Lightning UIs Fast — Scaffolded LWCs with Hob

Assembling a multi-step appointment booking wizard with reactive state machines, custom event composition, and Lightning App packaging using Hob.

Series Note: This article is Part 6 of our hands-on engineering series: Building the Salesforce Appointment Scheduler. Follow along as we design and implement a multi-location scheduling platform on Salesforce using enterprise patterns and developer-first tooling.


Beyond Boilerplate in Lightning Web Components

Building an enterprise user experience in Salesforce often involves complex UI state management. When coordination staff handle customer calls, they cannot afford a disjointed experience of bouncing between list views, related lists, and modal popups.

They need a guided, cohesive multi-step wizard:

  1. Identify or register the customer.
  2. Select the service requested.
  3. Discover the nearest branch and choose an available staff slot.
  4. Review and transactionally confirm the booking.

Historically, building an LWC wizard in Salesforce meant writing mountains of XML configuration (js-meta.xml), managing target configurations (lightning__AppPage, lightning__Tab), creating nested folders, and stitching together child components.

In Part 6, we accelerate the frontend build using Hob: The Salesforce House-Elf (hob create lwc) to scaffold our component hierarchy, wire up reactive state machines, and package the solution into a dedicated Lightning App.


Scaffolding the Component Hierarchy with Hob

A clean frontend architecture relies on component composition: a parent orchestrator managing state, and focused child components managing individual wizard steps.

We scaffolded the orchestrator and four step components in seconds:

# Parent Orchestrator (Exposed to Lightning App Builder & Tabs)
hob create lwc appointmentWizard \
  -l "Appointment Booking Wizard" \
  -d "Multi-step guided appointment booking flow for call center operators" \
  -t app tab

# Focused Step Components (Internal to the bundle)
hob create lwc customerSearchStep -l "Customer Search Step"
hob create lwc serviceSelectionStep -l "Service Selection Step"
hob create lwc slotPickerStep -l "Slot Picker Step"
hob create lwc bookingConfirmationStep -l "Booking Confirmation Step"

The Terminal Output

  ✨ Hob - The Salesforce House-Elf ✨
  "Loyal, quiet, and tireless assistance for your Salesforce Development."

✔ Created LWC 'appointmentWizard'

🪄 Hob crafted your Lightning Web Component with target configurations:

  Directory: force-app/main/default/lwc/appointmentWizard
  Files:     appointmentWizard.js, appointmentWizard.html, appointmentWizard.js-meta.xml
  Targets:   lightning__AppPage, lightning__Tab

Hob generates target tags automatically, eliminating the hassle of looking up Salesforce metadata strings. For internal child components, it sets isExposed = false, keeping the Lightning App Builder palette clean.


Architectural Blueprint: The Wizard State Machine

+-----------------------------------------------------------------------------------+
|                            appointmentWizard (Parent)                             |
|  - Tracks wizardState: step (1..4), customer, service, location, slot, notes      |
|  - Renders persistent context header (Selected Customer, Service, Branch)         |
|  - Controls lightning-progress-indicator progression                              |
+-----------------------------------------------------------------------------------+
       |                     |                     |                     |
       v                     v                     v                     v
+--------------+      +--------------+      +--------------+      +--------------+
|    Step 1    |      |    Step 2    |      |    Step 3    |      |    Step 4    |
|   Customer   |      |   Service    |      | Location and |      | Confirmation |
|    Search    |      |  Selection   |      | Slot Picker  |      |   & Recap    |
+--------------+      +--------------+      +--------------+      +--------------+

Context Persistence

As operators progress, a sticky context bar displays selections made so far, keeping critical context visible:

<template if:true={hasContext}>
    <div class="context-bar slds-m-bottom_medium slds-p-around_small slds-theme_shade slds-box slds-box_x-small">
        <template if:true={selectedCustomer}>
            <span class="context-pill">
                <lightning-icon icon-name="standard:contact" size="x-small"></lightning-icon>
                <strong>Customer:</strong> {selectedCustomer.Name} ({selectedCustomer.MailingPostalCode})
            </span>
        </template>
        <template if:true={selectedService}>
            <span class="context-pill">
                <lightning-icon icon-name="standard:service_resource" size="x-small"></lightning-icon>
                <strong>Service:</strong> {selectedService.Name} ({selectedService.Duration_Minutes__c} mins)
            </span>
        </template>
    </div>
</template>

Step-by-Step Component Implementation

Step 1: Customer Search & Inline Creation (customerSearchStep)

Call center operators handle two scenarios: existing customers calling back, or first-time callers needing instant registration.

customerSearchStep handles both in a side-by-side card layout:

  1. Search Pane: Dispatches to CustomerService.searchCustomers matching on Name, Email, Phone, or Postal Code. Results display cards with click-to-select actions.
  2. Quick Registration Form: Dispatches to CustomerService.createCustomer to register a new customer in a single click without leaving the booking screen.

When a customer is selected or registered, the component fires a clean custom event:

this.dispatchEvent(new CustomEvent('customerselected', {
    detail: customer,
    bubbles: true,
    composed: true
}));

Step 2: Service Selection (serviceSelectionStep)

Connects directly to AvailabilityService.getActiveServiceTypes to render the catalog of available appointment offerings:

  • Responsive grid of selectable service cards (slds-col slds-size_1-of-1 slds-medium-size_1-of-3).
  • Duration badges (30 mins, 60 mins, 120 mins) using SLDS badge styling.
  • Hover elevation and active border states for clear visual feedback.

Step 3: Location Discovery & Slot Selection (slotPickerStep)

This step brings our Part 4 Apex engines to life in real time:

  1. Proximity-Ranked Branches:
    • Immediately invokes LocationService.findNearestLocations using the customer’s postal code coordinates.
    • Branches are ranked by distance (e.g. 2.4 km away), automatically highlighting the closest branch.
  2. Shift-Aware Slot Picker:
    • The operator chooses a booking date (defaults to tomorrow).
    • Invokes AvailabilityService.getAvailableSlots({ locationId, serviceTypeId, slotDate }).
    • Renders interactive slot pills displaying the start time and the scheduled specialist.
  3. Operator Booking Notes:
    • Textarea for special customer requests or instructions.

Step 4: Summary Recap & Transactional Booking (bookingConfirmationStep)

Before writing to the database, the operator reviews all booking details in a structured card:

  • Customer details and contact information.
  • Selected branch location, address, and specialist.
  • Date, start time, end time, and duration.
  • Special notes.

When the operator clicks “Confirm & Book Appointment”:

  1. Invokes AppointmentService.bookAppointment asynchronously.
  2. Under the hood, this triggers:
    • FOR UPDATE row-locking concurrency protection (Part 4).
    • Double-booking conflict validation.
    • Automatic standard Task creation for the specialist (Part 5).
    • Dual customer and staff confirmation emails (Part 5).
  3. Upon success, the UI swaps into a confirmation screen displaying the AutoNumber reference (APT-0001) and a “Book Another Appointment” action to reset the wizard for the next caller.

Packaging the Experience: Custom Tabs & Lightning App

To make the wizard accessible to operators in Lightning Experience, we package the presentation layer:

1. Custom Lightning Component Tab (Appointment_Wizard.tab-meta.xml)

<?xml version="1.0" encoding="UTF-8"?>
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
    <description>Guided appointment booking wizard for service branches.</description>
    <label>Book Appointment</label>
    <lightningComponentBundle>appointmentWizard</lightningComponentBundle>
    <motif>Custom67: Gears</motif>
</CustomTab>

2. Custom Application (Appointment_Scheduler.app-meta.xml)

A dedicated standard Lightning Application brings together the booking wizard and core entities into a unified workspace:

  • App Name: Appointment Scheduler
  • Navigational Tabs:
    • Appointment_Wizard (Default landing tab)
    • Appointment__c (View all scheduled appointments)
    • Service_Type__c (Manage service catalog)
    • standard-Account (Branch locations)
    • standard-Contact (Customers and staff)

3. Granting Operator Permissions

We update Appointment_Scheduler_Operator.permissionset-meta.xml to grant access to the application and tabs:

<applicationVisibilities>
    <application>Appointment_Scheduler</application>
    <visible>true</visible>
</applicationVisibilities>

<tabSettings>
    <tab>Appointment_Wizard</tab>
    <visibility>Visible</visibility>
</tabSettings>
<tabSettings>
    <tab>Appointment__c</tab>
    <visibility>Visible</visibility>
</tabSettings>
<tabSettings>
    <tab>Service_Type__c</tab>
    <visibility>Visible</visibility>
</tabSettings>

DX Verdict & Tool Feedback

What Shined

  1. Target-Aware Scaffolding: hob create lwc <name> -t app tab eliminates the error-prone step of manually finding and typing the exact lightning__AppPage and lightning__Tab XML strings.
  2. Clean Component Defaults: Non-exposed child components are created instantly with isExposed = false, keeping the Lightning App Builder palette uncluttered.
  3. Rapid Iteration: Generating all five components took seconds, allowing immediate focus on state machine logic, CSS transitions, and Apex wiring.

Opportunities for Hob’s Backlog

  1. Scaffold Tabs from LWCs (--with-tab):
    • When scaffolding an LWC with -t tab, an optional flag --with-tab could automatically create the companion force-app/main/default/tabs/<Name>.tab-meta.xml file, eliminating another manual XML step.
  2. Jest Test Generation (--with-test):
    • Like hob create apex --with-test, supporting hob create lwc <name> --with-test to scaffold __tests__/<name>.test.js with standard @salesforce/sfdx-lwc-jest stubs would establish front-to-back TDD.
  3. Lightning App Scaffolding (hob create app):
    • A command like hob create app "Appointment Scheduler" --tabs Appointment_Wizard,Appointment__c would complete the full metadata generation suite.

The Appointment Scheduler Series

Written by Koster CX

Independent technical consultancy offering web hosting, modern web design, and custom software and Salesforce solutions.

Get in touch

Continue Reading

Related Articles

View all articles →