Livewire Layout System
Livewire Layout System
Overview
UltraViolet's Livewire integration includes a dedicated layout system built specifically for reactive components. The layout handles assets, scripts, styles, and provides a clean structure for building dynamic interfaces.
Layout File Structure
resources/views/layouts/
├── admin-livewire.blade.php # Main Livewire layout
└── partials/
├── header.blade.php # Header section
├── plugins.blade.php # Third-party scripts
├── cdn-scripts.blade.php # CDN resources
└── admin-action-bar.blade.php # Action bar
Main Layout: admin-livewire.blade.php
Location: resources/views/layouts/admin-livewire.blade.php
This is the master layout for all Livewire pages. It provides:
- ✅ Vite asset compilation
- ✅ Livewire scripts and styles
- ✅ @push/@stack support for component assets
- ✅ Menu system integration
- ✅ Breadcrumb support
- ✅ Flash messages
- ✅ Dark/light mode support
Basic Structure
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<!-- Styles -->
@vite(['resources/sass/admin-vertical.scss', 'resources/js/admin.ts'])
<!-- Component Styles -->
@stack('styles')
</head>
<body>
<!-- Navigation -->
@include('components.navigation.hui-vertical-menu')
<!-- Page Content -->
<div class="page-content">
@yield('content')
</div>
<!-- Scripts -->
@stack('scripts')
</body>
</html>
Vite Integration
UltraViolet uses Vite for asset compilation, providing lightning-fast builds and hot module replacement (HMR).
Configuration
vite.config.js:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/sass/admin-vertical.scss',
'resources/js/admin.ts',
],
refresh: true,
}),
],
});
In the Layout
@vite(['resources/sass/admin-vertical.scss', 'resources/js/admin.ts'])
This compiles and includes:
- SCSS → Compiled CSS with Bootstrap, UltraViolet theme, and component styles
- TypeScript/JS → Admin functionality, Livewire integration, third-party libs
Development vs Production
Development (with HMR):
npm run dev
Production (minified):
npm run build
Vite automatically handles:
- Asset hashing for cache busting
- Code splitting
- Tree shaking
- Minification
@push and @stack Directives
UltraViolet's layout uses Laravel's @push and @stack to manage component-specific assets.
Stacks Available
1. @stack('styles')
For component-specific CSS
2. @stack('scripts')
For component-specific JavaScript
How It Works
In Layout (admin-livewire.blade.php):
<head>
@vite(['resources/sass/admin-vertical.scss'])
@stack('styles') <!-- Component styles pushed here -->
</head>
<body>
@yield('content')
@stack('scripts') <!-- Component scripts pushed here -->
</body>
In Component View:
@extends('layouts.admin-livewire')
@section('content')
<div>
<!-- Your component content -->
</div>
@endsection
@push('styles')
<style>
.my-component {
background: #f0f0f0;
}
</style>
@endpush
@push('scripts')
<script>
console.log('Component loaded');
</script>
@endpush
Real Example: Vector Maps
@extends('layouts.admin-livewire')
@section('content')
<livewire:maps-vector />
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/jsvectormap@1.5.3/dist/js/jsvectormap.min.js"></script>
<script>
// Initialize map
const map = new jsVectorMap({
selector: '#map',
map: 'world'
});
// Listen for Livewire updates
Livewire.on('map-style-changed', (style) => {
map.setStyle(style);
});
</script>
@endpush
@push('styles')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jsvectormap@1.5.3/dist/css/jsvectormap.min.css">
@endpush
Asset Loading Order
Understanding the asset loading order is crucial:
1. Head Section
<head>
<!-- 1. Meta tags -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<!-- 2. Fonts (preconnect) -->
<link rel="preconnect" href="https://fonts.bunny.net">
<!-- 3. Vite CSS -->
@vite(['resources/sass/admin-vertical.scss'])
<!-- 4. Livewire Styles (auto-injected) -->
@livewireStyles
<!-- 5. Component-specific styles -->
@stack('styles')
</head>
2. Body Section
<body>
<!-- Content renders here -->
@yield('content')
<!-- 1. Vite JavaScript -->
@vite(['resources/js/admin.ts'])
<!-- 2. Livewire Scripts (auto-injected) -->
@livewireScripts
<!-- 3. Component-specific scripts -->
@stack('scripts')
</body>
Note: Livewire automatically injects @livewireStyles and @livewireScripts if not manually added.
Working with JavaScript
Livewire + Alpine.js
Livewire works seamlessly with Alpine.js (included in UltraViolet):
<div x-data="{ open: false }">
<button @click="open = !open">Toggle</button>
<div x-show="open" x-transition>
<livewire:my-component />
</div>
</div>
Dispatching Events to JavaScript
From Livewire Component:
<?php
namespace App\Livewire;
use Livewire\Component;
class MapController extends Component
{
public function changeStyle($style)
{
$this->dispatch('map-style-changed', style: $style);
}
}
In Blade View:
@push('scripts')
<script>
Livewire.on('map-style-changed', (event) => {
console.log('New style:', event.style);
updateMap(event.style);
});
</script>
@endpush
JavaScript → Livewire
@push('scripts')
<script>
// Call Livewire method from JavaScript
function saveData() {
@this.call('save', { data: 'value' });
}
// Update Livewire property
function updateCount() {
@this.set('count', 10);
}
// Get Livewire property
function getCount() {
const count = @this.get('count');
console.log(count);
}
</script>
@endpush
Component-Specific Styles
Method 1: Inline Styles
@push('styles')
<style>
.stats-widget {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
padding: 1.5rem;
}
</style>
@endpush
Method 2: External Stylesheet
@push('styles')
<link rel="stylesheet" href="asset-css-components-stats-widget.css.html">
@endpush
Method 3: Vite Import
In resources/js/admin.ts:
// Import component-specific styles
import './components/stats-widget.scss';
Best Practice: Use Vite imports for reusable component styles.
Third-Party Libraries
CDN Resources
For quick prototyping, use CDN (via @push):
@push('styles')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/library@1.0.0/dist/library.min.css">
@endpush
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/library@1.0.0/dist/library.min.js"></script>
@endpush
NPM Packages (Recommended)
For production, install via npm:
npm install library-name
Import in resources/js/admin.ts:
import LibraryName from 'library-name';
window.LibraryName = LibraryName;
Use in components:
@push('scripts')
<script>
const instance = new LibraryName({
// config
});
</script>
@endpush
Menu Integration
The Livewire layout integrates with UltraViolet's dynamic menu system.
Menu Configuration
config/livewire-menus.php:
<?php
return [
'admin' => [
[
'label' => 'Livewire Dash',
'route' => 'livewire.dashboard',
'icon' => 'flash_on',
'type' => 'simple',
],
[
'label' => 'Live Widgets',
'route' => '#',
'icon' => 'extension',
'type' => 'accordion',
'children' => [
[
'label' => 'Counter',
'route' => 'livewire.components.counter',
'icon' => 'add_circle',
],
// More items...
],
],
],
];
Rendering the Menu
In Layout:
@include('components.navigation.hui-vertical-menu', [
'menuConfig' => config('livewire-menus.admin')
])
Breadcrumbs
Defining Breadcrumbs
@extends('layouts.admin-livewire')
@section('breadcrumb-title', 'Livewire Dashboard')
@section('breadcrumbs')
<li class="breadcrumb-item"><a href="../uv-vertical/index.html">Home</a></li>
<li class="breadcrumb-item active">Livewire</li>
@endsection
@section('content')
<!-- Your content -->
@endsection
Custom Breadcrumb Component
<x-chevron-breadcrumb
:items="[
['label' => 'Home', 'url' => '/'],
['label' => 'Livewire', 'url' => '/livewire/dashboard'],
['label' => 'Components', 'active' => true]
]"
/>
Page Actions
Add action buttons to the page header:
@section('page-actions')
<div class="d-flex gap-2">
<button class="btn btn-primary" wire:click="save">
<i class="material-symbols-rounded">save</i>
Save
</button>
<button class="btn btn-outline-secondary" wire:click="cancel">
Cancel
</button>
</div>
@endsection
Flash Messages
Display success/error messages:
// In Component
public function save()
{
// Save logic...
session()->flash('message', 'Successfully saved!');
session()->flash('type', 'success'); // success, error, warning, info
}
@if (session()->has('message'))
<div class="alert alert-{{ session('type', 'info') }} alert-dismissible fade show">
{{ session('message') }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
Dark Mode Support
The layout automatically supports dark mode switching:
<body data-theme="{{ session('theme', 'dark') }}">
Toggle Dark Mode
<button onclick="toggleTheme()">
Toggle Theme
</button>
@push('scripts')
<script>
function toggleTheme() {
const body = document.body;
const current = body.dataset.theme;
const newTheme = current === 'dark' ? 'light' : 'dark';
body.dataset.theme = newTheme;
// Save preference
fetch('/api/preferences/theme', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ theme: newTheme })
});
}
</script>
@endpush
Loading States
Show loading indicators during Livewire requests:
<div>
<button wire:click="save">
<span wire:loading.remove wire:target="save">Save</span>
<span wire:loading wire:target="save">
<i class="spinner-border spinner-border-sm"></i>
Saving...
</span>
</button>
</div>
Global Loading Indicator
<div
wire:loading
class="position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center"
style="background: rgba(0,0,0,0.5); z-index: 9999;"
>
<div class="spinner-border text-light" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
Performance Optimization
1. Lazy Loading Components
<livewire:stats-widget lazy />
Component loads after initial page render.
2. Asset Preloading
<link rel="preload" href="asset-js-heavy-library.js.html" as="script">
3. Defer Non-Critical Scripts
@push('scripts')
<script src="analytics.js" defer></script>
@endpush
4. Inline Critical CSS
<style>
/* Critical CSS for above-the-fold content */
.hero { /* ... */ }
</style>
Complete Example
Here's a complete Livewire page with all features:
@extends('layouts.admin-livewire')
@section('breadcrumb-title', 'User Dashboard')
@section('breadcrumbs')
<li class="breadcrumb-item"><a href="../uv-vertical/index.html">Home</a></li>
<li class="breadcrumb-item active">Dashboard</li>
@endsection
@section('page-actions')
<button class="btn btn-primary" wire:click="refresh">
<i class="material-symbols-rounded">refresh</i>
Refresh
</button>
@endsection
@section('content')
<div class="row">
<div class="col-md-4">
<livewire:stats-widget lazy />
</div>
<div class="col-md-4">
<livewire:mini-counter />
</div>
<div class="col-md-4">
<livewire:quick-todo />
</div>
</div>
@endsection
@push('styles')
<style>
.dashboard-card {
border-radius: 12px;
transition: transform 0.2s;
}
.dashboard-card:hover {
transform: translateY(-4px);
}
</style>
@endpush
@push('scripts')
<script>
// Initialize tooltips
const tooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltips.forEach(el => new bootstrap.Tooltip(el));
// Listen for Livewire events
Livewire.on('stats-updated', (data) => {
console.log('Stats updated:', data);
});
</script>
@endpush
Troubleshooting
Assets Not Loading
Problem: CSS/JS not loading in development
Solution:
# Make sure Vite dev server is running
npm run dev
Livewire Not Working
Problem: Livewire directives not responding
Solution: Check that Livewire scripts are loaded:
@livewireScripts <!-- Add if missing -->
Script Execution Order
Problem: Scripts running before DOM ready
Solution: Use DOMContentLoaded:
document.addEventListener('DOMContentLoaded', function() {
// Your code here
});
Next Steps
- [Components Guide]({{ route('docs.show', 'livewire/components') }}) - Explore all 9 components
- [Best Practices]({{ route('docs.show', 'livewire/best-practices') }}) - Tips and patterns
- Vite Documentation - Learn more about Vite
The layout is your foundation. Master it, and building Livewire components becomes effortless! 🚀