Loader
Loaders (also called spinners or throbbers) provide visual feedback during asynchronous operations. They indicate that content is loading, a process is running, or the application is waiting for a response. Domma provides four animated loader types with flexible sizing, colouring, and positioning options.
Key Features
- Four Animation Types - Spinner, dots, pulse, and bars
- Flexible Sizing - Small, medium, large, or custom pixel size
- Theme-Aware Colours - Primary, secondary, success, danger, or custom hex
- Overlay Mode - Cover entire container with backdrop
- Loading Text - Optional text label below animation
- Static Helpers - Fullscreen loader and quick show/hide methods
When to Use Loaders
- API Calls - Show whilst fetching data from a server
- File Uploads - Indicate progress during file transfers
- Page Transitions - Display whilst loading new content
- Heavy Computations - Provide feedback during intensive JavaScript operations
Basic Usage
<!-- HTML Container -->
<div id="my-container" class="demo-area-min-h-120">
<!-- Loader will be inserted here -->
</div>
<!-- JavaScript Initialisation -->
<script>
const loader = Domma.elements.loader('#my-container', {
type: 'spinner', // 'spinner', 'dots', 'pulse', 'bars'
size: 'medium', // 'small', 'medium', 'large', or number (px)
color: 'primary' // 'primary', 'secondary', 'white', or hex
});
// Show/hide
loader.show();
loader.hide();
loader.toggle();
</script>
Quick Start
Get started with Loaders in seconds. Here's the simplest implementation:
Basic Spinner Loader
Create a container and initialise a loader:
<div id="my-loader-container" class="demo-area-min-h-120">
<!-- Loader appears here -->
</div>
<script>
const loader = Domma.elements.loader('#my-loader-container', {
type: 'spinner',
size: 'medium',
color: 'primary'
});
loader.show(); // Display the loader
</script>
Static Fullscreen Loader
Quick fullscreen loader with a single line:
// Show fullscreen loader for 2 seconds
const fsLoader = Domma.elements.fullscreenLoader('Loading content...');
setTimeout(() => fsLoader.destroy(), 2000);
Step-by-Step Tutorial
Let's build a data fetching interface that shows a loader whilst making an API call, then displays the results.
Step 1: Create the HTML Structure
Create a container with a button to trigger loading:
<button id="load-data-btn" class="btn btn-primary">Load Data</button>
<div id="data-container" class="demo-area mt-4"
class="demo-area-min-h-200">
<p class="text-center text-muted" id="placeholder-text">
Click "Load Data" to fetch content
</p>
<div id="data-result" class="hidden"></div>
</div>
Step 2: Create the Loader Instance
Initialise a loader with overlay to block interaction:
const dataLoader = Domma.elements.loader('#data-container', {
type: 'spinner',
size: 'large',
color: 'var(--dm-white)',
overlay: true, // Semi-transparent backdrop
text: 'Loading data...' // Text below spinner
});
Step 3: Show Loader During Async Operation
Display the loader whilst fetching data:
async function loadData() {
// Hide placeholder
document.getElementById('placeholder-text').classList.add('hidden');
// Show loader
dataLoader.show();
try {
// Simulate API call
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// Process data...
} catch (error) {
console.error('Failed to load data:', error);
}
}
Step 4: Hide Loader and Display Results
Hide the loader and show fetched data:
async function loadData() {
document.getElementById('placeholder-text').classList.add('hidden');
dataLoader.show();
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// Hide loader
dataLoader.hide();
// Display results
const resultEl = document.getElementById('data-result');
resultEl.innerHTML = `<h4>Data Loaded!</h4><pre>${JSON.stringify(data, null, 2)}</pre>`;
resultEl.classList.remove('hidden');
} catch (error) {
dataLoader.hide();
alert('Failed to load data');
}
}
Step 5: Update Loader Text Dynamically
Change the loading message based on progress:
async function loadMultipleResources() {
dataLoader.setText('Loading users...');
dataLoader.show();
const users = await fetch('/api/users').then(r => r.json());
dataLoader.setText('Loading posts...');
const posts = await fetch('/api/posts').then(r => r.json());
dataLoader.setText('Loading comments...');
const comments = await fetch('/api/comments').then(r => r.json());
dataLoader.hide();
// Display all data...
}
Step 6: Complete Working Demo
Try the complete data loading example:
Click "Load Data" to fetch content
Examples & Variations
Four Loader Types
Domma provides four different animation styles:
Spinner
Dots
Pulse
Bars
// Spinner (default)
Domma.elements.loader('#spinner-container', { type: 'spinner' }).show();
// Animated dots
Domma.elements.loader('#dots-container', { type: 'dots' }).show();
// Pulsing circle
Domma.elements.loader('#pulse-container', { type: 'pulse' }).show();
// Vertical bars
Domma.elements.loader('#bars-container', { type: 'bars' }).show();
Size Variants
Small, medium, and large sizes, plus custom pixel values:
Small (24px) | Medium (40px) | Large (64px) | Custom (80px)
// Small (24px)
Domma.elements.loader('#container', { size: 'small' }).show();
// Medium (40px - default)
Domma.elements.loader('#container', { size: 'medium' }).show();
// Large (64px)
Domma.elements.loader('#container', { size: 'large' }).show();
// Custom pixel size
Domma.elements.loader('#container', { size: 80 }).show();
Colour Variants
Theme-aware colours or custom hex values:
Primary | Success | Danger | Warning | Dark | Custom (#9b59b6)
// Theme colours
Domma.elements.loader('#container', { color: 'primary' }).show();
Domma.elements.loader('#container', { color: 'success' }).show();
Domma.elements.loader('#container', { color: 'danger' }).show();
Domma.elements.loader('#container', { color: 'warning' }).show();
// Custom hex colour
Domma.elements.loader('#container', { color: 'var(--dm-primary)' }).show();
Overlay Mode
Cover container with semi-transparent backdrop:
Content Being Loaded
This content is blocked by the overlay loader.
const overlayLoader = Domma.elements.loader('#container', {
type: 'spinner',
size: 'large',
color: 'var(--dm-white)',
overlay: true, // Enable backdrop overlay
text: 'Loading content...'
});
// Show for 2 seconds
overlayLoader.show();
setTimeout(() => overlayLoader.hide(), 2000);
With Loading Text
Add descriptive text below the loader animation:
const textLoader = Domma.elements.loader('#container', {
type: 'dots',
size: 'medium',
color: 'primary',
text: 'Loading your data...'
});
textLoader.show();
// Update text dynamically
setTimeout(() => textLoader.setText('Almost there...'), 1500);
Static Helper Methods
Quick show/hide without creating instances:
Click "Show Loader" to display
// Quick show (creates loader if needed)
Domma.elements.showLoader('#container');
// Quick hide
Domma.elements.hideLoader('#container');
// Fullscreen loader
const fs = Domma.elements.fullscreenLoader('Processing...');
setTimeout(() => fs.destroy(), 2000);
Button Loading State
Show loader inside a button during submission:
const submitBtn = document.getElementById('submit-btn');
let btnLoader = null;
submitBtn.addEventListener('click', async () => {
// Disable button and show loader
submitBtn.disabled = true;
submitBtn.textContent = '';
btnLoader = Domma.elements.loader(this, {
type: 'spinner',
size: 20,
color: 'var(--dm-white)',
centered: true
});
btnLoader.show();
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000));
// Re-enable button
btnLoader.destroy();
submitBtn.disabled = false;
submitBtn.textContent = originalText;
});
Real-World Example: Image Gallery Loader
Load and display images with a progress loader:
Click "Load Gallery" to fetch images
const galleryContainer = document.getElementById('gallery-container');
const galleryLoader = Domma.elements.loader(galleryContainer, {
type: 'pulse',
size: 'large',
color: 'primary',
overlay: true,
text: 'Loading gallery...'
});
async function loadGallery() {
galleryContainer.innerHTML = '';
galleryLoader.show();
try {
// Fetch image URLs
const response = await fetch('/api/images');
const images = await response.json();
galleryLoader.setText(`Loading ${images.length} images...`);
// Load images
const imgPromises = images.map(url => {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(img);
img.src = url;
});
});
await Promise.all(imgPromises);
// Hide loader and display images
galleryLoader.hide();
images.forEach(url => {
const imgEl = document.createElement('img');
imgEl.src = url;
imgEl.className = 'w-full h-auto rounded';
galleryContainer.appendChild(imgEl);
});
} catch (error) {
galleryLoader.hide();
alert('Failed to load gallery');
}
}
Best Practices
Accessibility
- ARIA Attributes - Loaders automatically include
role="status"andaria-live="polite"for screen readers - Loading Text - Always provide descriptive text via the
textoption for screen reader users - Focus Management - When showing overlay loaders, consider trapping focus or announcing the loading state
- Colour Contrast - Ensure loader colour has sufficient contrast (4.5:1) against background, especially with overlay mode
- Timeout Handling - Provide fallback error messages if loading takes too long or fails
- Keyboard Users - Don't rely solely on loaders - provide skip links or cancel buttons for long operations
Performance
- Destroy When Done - Call
loader.destroy()after use to remove DOM elements and prevent memory leaks - Reuse Instances - Create loader once, then show/hide repeatedly instead of recreating:
loader.show() / loader.hide() - CSS Animations - Loaders use CSS animations (not JavaScript), which are GPU-accelerated and performant
- Lazy Initialisation - Don't create loaders on page load - initialise only when needed
- Debounce Quick Operations - For operations under 300ms, consider skipping the loader to avoid flashing
Common Gotchas
-
Issue: Loader not visible or positioned incorrectly
Solution: Ensure container hasposition: relativeandmin-height
Example:- Issue: Loader stays visible after operation completes
Solution: Always callloader.hide()in both success and error handlers
Example: Use try/finally or .finally() to ensure hide() is called- Issue: Multiple loaders stacking in same container
Solution: Destroy previous loader before creating new one, or reuse same instance
Example:existingLoader?.destroy(); newLoader = Domma.elements.loader(...)- Issue: Fullscreen loader not covering modal/dropdown
Solution: Fullscreen loader has z-index: 9999. Ensure modals don't exceed this
Example: Adjust modal z-index or create loader inside modal- Issue: setText() not working
Solution: Must initialise loader withtextoption first, or setText() creates new element
Example:{ text: 'Initial text...' }thenloader.setText('New text')Tips & Tricks
- Minimum Display Time - Show loaders for at least 500ms to avoid jarring flashes on fast connections
- White Overlay - Use
color: 'white'withoverlay: truefor dark backdrops - Debounce Search - Combine with
_.debounce()for live search: show loader only if typing pauses - Progress Updates - Use
setText()to show incremental progress: "Step 1 of 3..." - Container Sizing - For inline loaders, set explicit container dimensions to prevent layout shift
- Cleanup in SPA - Always destroy loaders when unmounting components/pages in single-page apps
<!-- Accessible Loader with Proper Container --> <div id="data-container" role="region" aria-label="Data content" style="position: relative; min-height: 200px;"> <!-- Content --> </div> <script> const loader = Domma.elements.loader('#data-container', { type: 'spinner', size: 'medium', color: 'primary', overlay: true, text: 'Loading data, please wait...' // Screen reader accessible }); async function fetchData() { loader.show(); try { const data = await fetch('/api/data').then(r => r.json()); // Ensure minimum display time await new Promise(resolve => setTimeout(resolve, 500)); // Process data... } catch (error) { console.error('Failed:', error); alert('Failed to load data. Please try again.'); } finally { // Always hide loader, even on error loader.hide(); } } </script>API Reference
Constructor Options
Option Type Default Description typeString 'spinner'Animation type: 'spinner','dots','pulse', or'bars'sizeString | Number 'medium'Size: 'small'(24px),'medium'(40px),'large'(64px), or custom pixel numbercolorString 'primary'Colour: 'primary','secondary','success','danger','warning','info','white','dark', or custom hexoverlayBoolean falseShow with semi-transparent backdrop covering entire container textString ''Optional loading text displayed below animation centeredBoolean trueCentre loader in container. If false, loader displays inline Instance Methods
Method Parameters Returns Description show()- this Display the loader. Chainable hide()- this Hide the loader. Chainable toggle()- this Toggle visibility (show if hidden, hide if visible). Chainable Static Methods
Method Parameters Returns Description Domma.elements.showLoader(selector, options)String | Element, Object Loader instance Quick show loader in container. Creates instance if needed, reuses if exists Domma.elements.hideLoader(selector)String | Element void Quick hide loader in container. No-op if loader doesn't exist Domma.elements.fullscreenLoader(text, options)String, Object Loader instance Show fullscreen loader covering entire viewport. Call instance.destroy()to removeCSS Classes
Class Description .dm-loaderMain wrapper element containing loader animation and optional text .dm-loader-spinnerCircular spinning animation element .dm-loader-dotsThree bouncing dots animation container .dm-loader-pulsePulsing circle animation element .dm-loader-barsFour vertical bars animation container // Complete API Example const loader = Domma.elements.loader('#my-container', { type: 'spinner', size: 'large', color: 'primary', overlay: true, text: 'Loading...', centered: true }); // Instance methods loader.show(); // Display loader loader.hide(); // Hide loader loader.toggle(); // Toggle visibility const visible = loader.isVisible(); // Check visibility → true/false loader.setText('Almost done...'); // Update text loader.destroy(); // Clean up // Static methods Domma.elements.showLoader('#container'); // Quick show Domma.elements.hideLoader('#container'); // Quick hide const fs = Domma.elements.fullscreenLoader('Processing...'); setTimeout(() => fs.destroy(), 2000); // Remove after 2sConfig Engine Integration
Use Domma's declarative
$.setup()system to configure Loaders. Note: Loaders typically need imperative control (show/hide), but you can pre-configure them with the Config Engine.Pre-Configure Loader
// Pre-configure loader, then control manually let dataLoader; $.setup({ '#data-container': { component: 'loader', options: { type: 'spinner', size: 'large', color: 'var(--dm-white)', overlay: true, text: 'Loading data...' }, initial: { // Loader hidden by default } } }); // Get instance and control manually dataLoader = Domma.elements.get('#data-container'); async function fetchData() { dataLoader.show(); const data = await fetch('/api/data').then(r => r.json()); dataLoader.hide(); }Event-Triggered Loader
// Show loader on button click, hide after delay $.setup({ '#overlay-container': { component: 'loader', options: { type: 'pulse', size: 'large', color: 'var(--dm-white)', overlay: true, text: 'Loading content...' } }, '#load-btn': { events: { click: (e, $el) => { const loader = Domma.elements.get('#overlay-container'); loader.show(); // Simulate async operation setTimeout(() => { loader.setText('Almost done...'); }, 1000); setTimeout(() => { loader.hide(); }, 2000); } } } });Live Demo
Loader configured via Config Engine:
Click button to show loader configured via Config Engine
// Configuration $.setup({ '#config-demo-container': { component: 'loader', options: { type: 'dots', size: 'medium', color: 'primary', overlay: true, text: 'Config Engine Loader' } }, '#config-trigger-btn': { events: { click: () => { const loader = Domma.elements.get('#config-demo-container'); loader.show(); setTimeout(() => loader.hide(), 2000); } } } });Related Elements
Modal
Dialogue windows with backdrops. Combine with loaders to show loading state inside modal content areas.
Toast
Temporary notifications. Use toasts to notify users when background operations complete after showing a loader.
Button Group
Connected button controls. Show loaders inside buttons during form submissions or async actions.
Card
Content containers. Use overlay loaders within cards to indicate loading state for card-specific content.
- Issue: Loader stays visible after operation completes