Skip to main content

Overview

The Lazy Loading example demonstrates how to improve application performance by loading components on-demand:
  • Lazy component loading with lazy()
  • Custom loading components
  • Dynamic imports
  • Performance optimization

Live Demo

View the complete source code on GitHub

What You’ll Learn

Lazy Loading

Load components only when needed

Custom Loading

Create custom loading indicators

Code Splitting

Split code for better performance

Suspense Pattern

Handle async component loading

Complete Code

Setting Up Lazy Components

lazy-demo.js
import {
  Component,
  createComponent,
  h,
  lazy,
  createDelayedComponent
} from "@glyphui/runtime";

// Regular component (loaded immediately)
class HomePage extends Component {
  render() {
    return h('div', { class: 'card' }, [
      h('h2', {}, ['Home']),
      h('p', {}, [
        'This component was loaded immediately because it\'s the default view.'
      ])
    ]);
  }
}

// Components to be lazy loaded
class DashboardPage extends Component {
  constructor(props) {
    super(props, {
      initialState: {
        widgets: [
          { id: 1, title: 'Revenue', value: '$12,345', color: '#4CAF50' },
          { id: 2, title: 'Users', value: '891', color: '#2196F3' },
          { id: 3, title: 'Conversion', value: '3.2%', color: '#FFC107' },
          { id: 4, title: 'Bounce Rate', value: '42%', color: '#F44336' }
        ]
      }
    });
  }
  
  render(props, state) {
    return h('div', { class: 'card' }, [
      h('h2', {}, ['Dashboard']),
      h('p', {}, ['This dashboard component was loaded lazily!']),
      h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '10px' } }, 
        state.widgets.map(widget => 
          h('div', { 
            key: widget.id,
            style: { 
              padding: '15px', 
              borderRadius: '4px',
              backgroundColor: widget.color,
              color: 'white',
              width: '45%'
            } 
          }, [
            h('h3', {}, [widget.title]),
            h('div', { 
              style: { fontSize: '24px', fontWeight: 'bold' } 
            }, [widget.value])
          ])
        )
      )
    ]);
  }
}

class ProfilePage extends Component {
  constructor(props) {
    super(props, {
      initialState: {
        user: {
          name: 'Jane Doe',
          email: '[email protected]',
          role: 'Administrator'
        }
      }
    });
  }
  
  render(props, state) {
    const { user } = state;
    return h('div', { class: 'card' }, [
      h('h2', {}, ['User Profile']),
      h('p', {}, ['This profile component was loaded lazily.']),
      h('div', { style: { marginTop: '20px' } }, [
        h('p', {}, [`Email: ${user.email}`]),
        h('p', {}, [`Role: ${user.role}`])
      ])
    ]);
  }
}

Custom Loading Component

class CustomLoadingComponent extends Component {
  constructor(props) {
    super(props, {
      initialState: { dots: 1 }
    });
    
    // Animate loading dots
    this.interval = setInterval(() => {
      this.setState({ dots: (this.state.dots % 3) + 1 });
    }, 300);
  }
  
  beforeUnmount() {
    clearInterval(this.interval);
  }
  
  render() {
    const { dots } = this.state;
    const dotsStr = '.'.repeat(dots);
    
    return h('div', { 
      style: { 
        padding: '30px', 
        textAlign: 'center',
        background: '#f9f9f9',
        borderRadius: '4px',
        margin: '20px 0' 
      } 
    }, [
      h('div', { 
        style: { fontSize: '20px', marginBottom: '10px' } 
      }, [`Loading${dotsStr}`]),
      h('div', { 
        style: { 
          height: '4px', 
          width: '100%', 
          backgroundColor: '#eee',
          borderRadius: '2px',
          overflow: 'hidden'
        } 
      }, [
        h('div', { 
          style: { 
            height: '100%', 
            width: `${(dots / 3) * 100}%`, 
            backgroundColor: '#4CAF50',
            transition: 'width 0.3s ease-in-out'
          } 
        })
      ])
    ]);
  }
}

Main Application with Lazy Loading

class App extends Component {
  constructor() {
    super({}, {
      initialState: {
        currentPage: 'home',
        useCustomLoading: true,
        loadingDelay: 1500
      }
    });
    
    this.initializeLazyComponents();
  }
  
  initializeLazyComponents() {
    const { useCustomLoading, loadingDelay } = this.state;
    
    const loadingOptions = useCustomLoading 
      ? { loading: CustomLoadingComponent }
      : {};
    
    this.lazyComponents = {
      home: HomePage,
      dashboard: lazy(
        createDelayedComponent(DashboardPage, loadingDelay),
        loadingOptions
      ),
      profile: lazy(
        createDelayedComponent(ProfilePage, loadingDelay),
        loadingOptions
      ),
      settings: lazy(
        createDelayedComponent(SettingsPage, loadingDelay),
        loadingOptions
      )
    };
  }
  
  navigateTo(page) {
    this.setState({ currentPage: page });
  }
  
  render(props, state) {
    const { currentPage } = state;
    
    if (currentPage === 'home') {
      return createComponent(this.lazyComponents.home);
    } else {
      const lazyFactory = this.lazyComponents[currentPage];
      return lazyFactory();
    }
  }
}

const app = new App();
app.mount(document.getElementById('content'));

Key Concepts

1. Lazy Function

The lazy() function creates a lazy-loaded component wrapper:
const LazyComponent = lazy(
  createDelayedComponent(DashboardPage, 1500),
  { loading: CustomLoadingComponent }
);
In production, you would use dynamic imports instead of createDelayedComponent().

2. Loading States

Provide a loading component to display while loading:
{ loading: CustomLoadingComponent }
Without a custom loading component, nothing is displayed during loading.

3. Component Factory Pattern

Lazy components return a factory function:
const lazyFactory = this.lazyComponents[currentPage];
return lazyFactory(); // Call factory to get component

4. Cleanup in Loading Components

Always clean up resources when unmounting:
beforeUnmount() {
  clearInterval(this.interval);
}

Benefits of Lazy Loading

Only load the code needed for the initial view, reducing the JavaScript bundle size that users download on first load.
By splitting code into smaller chunks, the initial page load is faster, improving perceived performance.
Components are only loaded when needed, reducing memory usage and CPU time spent parsing unused JavaScript.
Users can start interacting with the app sooner, while additional features load in the background.

Production Usage

In a real application with a bundler, you’d use dynamic imports:
// Instead of createDelayedComponent
const LazyDashboard = lazy(
  () => import('./pages/Dashboard'),
  { loading: LoadingSpinner }
);
The bundler will automatically code-split at these import boundaries.

Running the Example

1

Clone the repository

git clone https://github.com/x0bd/glyphui.git
cd glyphui/examples/lazy-loading
2

Open in browser

Open index.html in your browser:
npx serve .
3

Test lazy loading

  • Click different navigation buttons
  • Watch the loading indicator appear
  • Notice the delay before components render
  • Try toggling loading settings

Next Steps

Performance

Learn more optimization techniques

Pomodoro Timer

See advanced hooks and effects

Lazy API

Detailed lazy() documentation

Lifecycle

Component lifecycle methods

Build docs developers (and LLMs) love