A modern, lightweight web application built with Alpine.js, Vite, and TailwindCSS that demonstrates advanced component architecture patterns, lazy loading, and inter-component communication.
| Technology | Purpose | Version |
|---|---|---|
| Alpine.js ποΈ | Reactive frontend framework | 3.14.9 |
| Vite β‘ | Build tool and dev server | 6.3.5 |
| TailwindCSS π¨ | Utility-first CSS framework | 4.1.10 |
| Alpine Intersect ποΈ | Intersection Observer plugin | 3.14.9 |
| HTML Inject Plugin π§ | Component template injection | 1.1.2 |
alpineJS/
βββ π index.html # Main entry point
βββ π¦ package.json # Dependencies & scripts
βββ βοΈ vite.config.mjs # Vite configuration
βββ src/
βββ π¨ assets/
β βββ css/style.css # Global styles
β βββ js/
β βββ app.js # Main app initialization
β βββ components/ # Alpine.js components
β βββ slider.js # Image slider component
β βββ sliderDisplay.js # Slider status display
β βββ lightbox.js # Image lightbox modal
βββ π data/
β βββ slides.json # Slider content data
βββ π§© parts/
βββ header.html # Header template
βββ slider.html # Slider template
βββ lightbox.html # Lightbox template
All components are registered in src/assets/js/app.js:
import Alpine from 'alpinejs'
import Slider from './components/slider.js'
import SliderDisplay from './components/sliderDisplay.js'
import Lightbox from './components/lightbox.js'
// Register components globally
Alpine.data('slider', Slider)
Alpine.data('sliderDisplay', SliderDisplay)
Alpine.data('lightbox', Lightbox)Each component follows a factory function pattern that returns an Alpine.js data object:
export default function ComponentName(parameters) {
return {
// Reactive properties
property: initialValue,
// Lifecycle method
init() {
// Component initialization logic
},
// Component methods
method() {
// Component functionality
}
};
}Templates are modularized using the HTML Inject Plugin:
- β Separation of Concerns: HTML structure separated from logic
- β Reusability: Components can be used across different pages
- β Maintainability: Each component has its own template file
<!-- index.html -->
<load src="src/parts/slider.html" />
<load src="src/parts/lightbox.html" />The project implements intelligent image lazy loading using Alpine's Intersect plugin:
<img x-intersect.once="$el.src = slide.image"
:alt="slide.title"
class="w-full h-full object-cover">How it works:
- π Intersection Observer: Monitors when image containers enter viewport
- β‘ On-Demand Loading: Images load only when visible
- π― Once Modifier: Each image loads only once to prevent re-triggering
- π Performance Boost: Reduces initial page load time and bandwidth
Slider data is fetched asynchronously:
async loadSlides() {
try {
const response = await fetch(sliderJson);
this.slides = await response.json();
this.isLoading = false;
} catch (error) {
console.error('Error loading slides:', error);
this.isLoading = false;
}
}Benefits:
- β±οΈ Non-blocking: UI remains responsive during data loading
- π Loading States: Visual feedback with loading indicators
- π‘οΈ Error Handling: Graceful degradation on fetch failures
The project uses Alpine's custom event system for component communication:
// Slider component dispatches events
this.$dispatch('slider-ready', {
current: this.current,
total: this.slides.length
});
this.$dispatch('slider-changed', {
current: nextSlideEq,
total: this.slides.length
});
this.$dispatch('open-lightbox', {
image: this.slides[index]
});Method 1: Component-based listening
// SliderDisplay component
init() {
window.addEventListener('slider-ready', this.handleSliderEvent.bind(this));
window.addEventListener('slider-changed', this.handleSliderEvent.bind(this));
}Method 2: Template-based listening
<div x-data="{ current: 0, total: 0 }"
@slider-ready.window="current = $event.detail.current; total = $event.detail.total"
@slider-changed.window="current = $event.detail.current; total = $event.detail.total">
</div>Method 3: Cross-component communication
<div x-data="lightbox()"
@open-lightbox.window="open($event.detail.image)">
</div>βββββββββββββββ slider-ready ββββββββββββββββββββ
β Slider β ββββββββββββββββββββΊ β SliderDisplay β
β Component β β Component β
β β slider-changed β β
β β ββββββββββββββββββββΊ β β
βββββββββββββββ ββββββββββββββββββββ
β
β open-lightbox
βΌ
βββββββββββββββ
β Lightbox β
β Component β
βββββββββββββββ
- π― Publisher-Subscriber: Slider broadcasts state changes
- π Command Pattern: Lightbox receives open commands
- π State Synchronization: Multiple displays stay in sync
- π Loose Coupling: Components don't directly reference each other
- Node.js (v18+)
- npm or yarn
# Clone the repository
git clone https://github.com/Loubal70/AlpinePerfFormation
cd alpineJS
# Install dependencies
npm install
# or
yarn install# Start development server
npm run dev
# or
yarn dev# Build for production
npm run build
# or
yarn build- β‘ Smooth Transitions: CSS-based animations
- π±οΈ Multiple Controls: Arrows, dots, and keyboard navigation
- π± Responsive Design: Mobile-friendly interface
- π Auto-loop: Infinite scrolling capability
- β¨οΈ Keyboard Support: Space bar to close
- π Elegant Animations: Smooth open/close transitions
- π Content Display: Title and description overlay
- π« Body Scroll Lock: Prevents background scrolling
- π Real-time Updates: Shows current slide position
- π‘ Event-driven: Updates via custom events
- π― Multiple Instances: Different display methods demonstrated
- β‘ Performance: Lazy loading and minimal bundle size
- π§© Modularity: Component-based architecture
- π Reactivity: Real-time UI updates
- π± Responsive: Mobile-first design approach
- π οΈ Developer Experience: Hot reload and modern tooling
- π¨ Maintainable: Clean separation of concerns
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
This project is open source and available under the MIT License.
Built with β€οΈ using Alpine.js and modern web technologies