Advertisement

AI Analysis

Livewire 4 Upgrade Plan

Overview

This document outlines the comprehensive plan for upgrading RiftSurge from Livewire 3 to Livewire 4. Livewire 4 is now officially released and brings significant improvements to animations, performance, and developer experience.

Official Resources:


πŸš€ Implementation Status

Completed βœ…

Phase

Task

Status

1.1

Update composer.json to Livewire ^4.0

βœ… Done

1.2

Update Flux UI to ^2.3 / Flux Pro to ^2.7

βœ… Done

1.3

Migrate $listeners to #[On()]

βœ… Done (14 files)

1.4

Fix Livewire.on() JS callback signature

βœ… Done (2 files)

2.1

Add wire:transition to custom modals

βœ… Done (6 files)

2.2

Improve loading states with delay modifiers

βœ… Done (4 files)

4.0

Implement lazy loading for heavy components

βœ… Done (13 components)

7.0

Migrate $queryString to #[Url] attributes

βœ… Done (5 files)

Remaining πŸ“‹

Phase

Task

Status

1.0

Run composer update

⏳ Requires local env

3.0

Test all updated components

⏳ After composer update

5.0

Add Islands for static content

⏸️ Not applicable (see below)

6.0

Implement wire:stream for AI responses

πŸ“‹ Optional (current pattern is fine)

Breaking Changes Audit

βœ… Fixed

Pattern

Status

Notes

protected $listeners

βœ… Fixed

All 14 files migrated to #[On()]

Livewire.on() JS callbacks

βœ… Fixed

Updated to use array destructuring

$this->emit()

βœ… N/A

Not used (using $this->dispatch())

$this->emitTo()

βœ… N/A

Not used

$this->dispatchBrowserEvent()

βœ… N/A

Not used

wire:model.defer

βœ… N/A

Not used (now default behavior)

wire:model.lazy

βœ… N/A

Not used

⚠️ Still Supported (Optional Migration)

Pattern

Files

Notes

protected $queryString

βœ… Migrated

All 5 files now use #[Url] attribute

protected $rules

4 files

Can migrate to rules() method

protected $messages

2 files

Can migrate to messages() method

getListeners()

3 files

Used for dynamic Echo listeners, still valid

Patterns Already Using Modern Syntax βœ…

Pattern

Status

$this->dispatch()

βœ… Correct

#[Computed]

βœ… Correct

#[On()] attributes

βœ… Correct

#[Url] attributes

βœ… Some components already use this

wire:navigate

βœ… Correct

@entangle

βœ… Correct

How to Complete the Upgrade

1. Pull the changes

git pull origin cursor/livewire-4-upgrade-61fb

2. Update all dependencies (Livewire 4 + Flux UI)

composer update livewire/livewire livewire/flux livewire/flux-pro --with-all-dependencies

3. Run Pint to format any changes

vendor/bin/pint --dirty

4. Run tests to verify

php artisan test

5. Test manually in the browser

Key Livewire 4 Features to Leverage

1. wire:transition (Implemented βœ…)

Smooth enter/leave animations for conditionally rendered elements:

@if($showModal)

Modal content

@endif

2. Loading State Improvements (Implemented βœ…)

New delay modifiers prevent UI flashing:

Saving...

3. wire:stream (Planned)

Perfect for AI-generated content streaming:

{{ $aiResponse }}

4. Lazy Loading (Implemented βœ…)

Defer component loading until visible with skeleton placeholders:


Loading skeleton...

Implemented in:

  • Surge Hub (9 components): improvement-trajectory, team-health-dashboard, strategic-intel, player-spotlight, weekly-podcast, performance-pulse, daily-missions, role-transition-context, playstyle-mismatch-alerts
  • Dashboard (4 components): upcoming-scrims, daily-message, comps, recent-activity

5. Islands (Evaluated - Not Applicable)

After analysis, Islands are not recommended for this codebase because:

1. Most components are interactive - Dashboard widgets, Surge Hub components, and other panels have significant user interactions (buttons, modals, forms) that require immediate hydration

2. Lazy loading provides similar benefits - We've already implemented lazy loading with skeleton placeholders for 13 heavy components, which provides the same initial page load performance benefits

3. Alpine.js state - Many components use @entangle, x-data, and complex Alpine state that wouldn't work well with deferred hydration

Islands are best for:

  • Tooltips that appear on hover
  • Help dropdowns that expand on click
  • Navigation menus that open on interaction
  • Static info panels that become interactive on focus

For this app, lazy loading is the better pattern because it defers component loading entirely until the component is in view, rather than deferring hydration of already-rendered HTML.


Current State Analysis

Codebase Statistics

Pattern

Count

Files

Livewire Components

97+

app/Livewire/

wire:model usages

211+

54 files

wire:loading usages

220+

32 files

wire:poll usages

10+

10 files

x-transition (Alpine)

27+

4 files

@entangle usages

10+

5 files

protected $listeners

14

14 files

#[On()] attributes

20

20 files

Echo integrations

8

8 files

Current Patterns in Use

  • Event Dispatching: Using $this->dispatch() (βœ… correct pattern)
  • Computed Properties: Using #[Computed] attribute (βœ… correct pattern)
  • Event Listeners: Mix of protected $listeners array and #[On()] attributes
  • Loading States: Heavy use of wire:loading, wire:loading.attr, wire:loading.class, wire:target
  • Real-time: Echo integration with #[On('echo:...')] listeners
  • Alpine Integration: @entangle, x-transition, $wire

Phase 1: Dependency Upgrade & Compatibility Check

1.1 Update Composer Dependencies

Update Livewire to v4

composer require livewire/livewire:^4.0

Flux UI should be compatible - verify version

composer require livewire/flux:^2.2 livewire/flux-pro:^2.6

1.2 Breaking Changes to Address

Based on Livewire 4 upgrade documentation:

Property Visibility (CRITICAL)

Livewire 4 requires public properties to be used with wire:model. Check all components for protected/private properties bound to forms.

Files to audit:

  • All components using wire:model
  • Components with form data

Event Listener Syntax

The protected $listeners array is deprecated. All listeners should use the #[On()] attribute.

Files requiring update (14 files):

app/Livewire/Dashboard.php

app/Livewire/Team/Show.php

app/Livewire/Stats/Show.php

app/Livewire/NotificationManager.php

app/Livewire/Scrims/CreatePosting.php

app/Livewire/Scrims/Find.php

app/Livewire/DraftInterface.php

app/Livewire/Games/Show.php

app/Livewire/Roster/Show.php

app/Livewire/Comps/BansKanban.php

app/Livewire/Comps/ChampionsKanban.php

app/Livewire/Comps/Bans.php

app/Livewire/Comps/Create.php

Example migration:

// Before (Livewire 3 - deprecated)

protected $listeners = [

'go-to-profile' => 'goToProfile',

'go-to-opgg' => 'goToOpgg',

];

// After (Livewire 4)

use Livewire\Attributes\On;

#[On('go-to-profile')]

public function goToProfile() { ... }

#[On('go-to-opgg')]

public function goToOpgg() { ... }

Alpine.js Version

Livewire 4 includes Alpine.js v3.14+. Ensure no manual Alpine imports conflict.


Phase 2: New Loading States Implementation

2.1 Overview of New Loading API

Livewire 4 introduces a more powerful and cleaner loading states API. Key improvements:

  • wire:loading.class with better specificity
  • wire:loading.delay with configurable delays (e.g., wire:loading.delay.500ms)
  • Better targeting with wire:target
  • New skeleton loading patterns

2.2 Files to Upgrade (32 files with wire:loading)

High-priority components (complex loading states):

1. livewire/games/fetch-game.blade.php - Game fetching with multiple loading states

2. livewire/roster/show.blade.php - 51 loading state usages

3. livewire/dashboard/podcast-widget.blade.php - 11 loading states

4. livewire/team/show.blade.php - 41 loading states

5. livewire/scrims/find.blade.php - 16 loading states

2.3 New Loading Patterns to Implement

Delay Modifiers

{{-- Show loading after 200ms delay to prevent flash --}}
Loading...

{{-- Different delay thresholds --}}

...
{{-- 50ms --}}
...
{{-- 100ms --}}
...
{{-- 150ms --}}
...
{{-- 300ms --}}
...
{{-- 500ms --}}
...
{{-- 1000ms --}}

Skeleton Loading Pattern

{{-- New skeleton loading for better UX --}}

{{ $actualContent }}

Loading States with Actions

{{-- Loading for specific actions --}}


Phase 3: Wire Transition (Animations)

3.1 Overview

Livewire 4's wire:transition replaces the need for both Alpine.js x-transition AND CSS transition- classes in many cases, providing smoother DOM morphing animations.

3.2 Transition Statistics

Type

Count

Files

CSS transition- classes

1,023+

152 files

Alpine x-transition

27+

4 files

Total candidates

1,050+

152 files

3.3 Files with Alpine x-transition

resources/views/livewire/persistent-podcast-player.blade.php (12 usages)

resources/views/components/trial-expiration-banner.blade.php (6 usages)

resources/views/components/podcast-player.blade.php (3 usages)

resources/views/components/subscription-expired-banner.blade.php (6 usages)

3.4 High-Impact CSS Transition Files

Files with most CSS transitions that may benefit from wire:transition:

File

CSS Transitions

Priority

livewire/draft/draft-lobby-view.blade.php

40

HIGH

livewire/draft/draft-lobby.blade.php

39

HIGH

livewire/flux-navbar.blade.php

34

MEDIUM

livewire/scrims/find.blade.php

32

HIGH

livewire/draft/create-draft-lobby.blade.php

23

MEDIUM

livewire/games/fetch-game.blade.php

23

HIGH

livewire/comps/show.blade.php

23

MEDIUM

livewire/roster/show.blade.php

22

HIGH

welcome/grid_features.blade.php

21

LOW

livewire/scrims/confirm.blade.php

19

HIGH

3.5 When to Migrate CSS Transitions

MIGRATE to wire:transition:

  • Elements shown/hidden via @if with Livewire state
  • Elements controlled by wire:loading
  • Lists that update via Livewire (with wire:key)

KEEP CSS transitions:

  • Hover effects (hover:scale-105)
  • Focus states (focus:ring-2)
  • Active states (active:scale-95)
  • Permanent decorative animations
  • Alpine-controlled visibility (use x-transition instead)

3.6 Migration Pattern for CSS Transitions

{{-- BEFORE: CSS transition on Livewire-controlled element --}}

@if($showPanel)

Panel content

@endif

{{-- AFTER: wire:transition handles enter/leave animation --}}

@if($showPanel)

Panel content

@endif

{{-- BEFORE: CSS transition on wire:loading element --}}

Save

{{-- AFTER: wire:transition for smoother morphing --}}

Save

3.3 Migration Examples

Before (Alpine x-transition)

x-transition:enter="transition ease-out duration-300"

x-transition:enter-start="opacity-0 scale-95"

x-transition:enter-end="opacity-100 scale-100"

x-transition:leave="transition ease-in duration-200"

x-transition:leave-start="opacity-100 scale-100"

x-transition:leave-end="opacity-0 scale-95">

After (Livewire wire:transition)

{{-- Using wire:transition for Livewire-controlled DOM updates --}}

@if($showModal)

@endif

3.4 Custom Animation Classes

{{-- Custom transition with specific duration --}}
...

{{-- Fade + scale combination --}}

...

{{-- Slide transitions --}}

...
...

3.5 Components to Enhance with wire:transition

Dashboard widgets:

  • livewire/dashboard/podcast-widget.blade.php - Add smooth transitions for state changes
  • livewire/dashboard/insights.blade.php - Transition between loading/loaded states
  • livewire/dashboard/daily-message.blade.php - Animate message updates

Game/Scrim flows:

  • livewire/games/show.blade.php - Smooth transitions for game states
  • livewire/scrims/confirm.blade.php - Better confirmation animations

Notification system:

  • livewire/notification-manager.blade.php - Enhanced notification animations

Phase 4: Livewire Islands

4.1 Overview

Islands allow you to isolate parts of your page that update independently, dramatically improving performance for complex pages.

4.2 Ideal Candidates for Islands

Dashboard Page (Dashboard.php):

The dashboard loads multiple independent widgets. Each can be an island:

{{-- Dashboard with Islands --}}

{{-- Left Column --}}

@island

@endisland

@island

@endisland

@island

@endisland

{{-- Right Column --}}

@island

@endisland

@island

@endisland

@island

@endisland

Surge Hub (Surge/Hub.php):

Complex page with many AI-powered widgets - perfect for islands:

{{-- Surge Hub with Islands for independent widget updates --}}

@island

@endisland

@island

@endisland

@island

@endisland

@island

@endisland

4.3 Benefits of Islands

1. Reduced Re-renders: Only the island updates, not the entire page

2. Better Performance: Smaller payloads for each update

3. Improved UX: Smoother experience with isolated updates

4. Parallel Loading: Islands can load/refresh independently

4.4 Pages to Convert to Islands

Page

Components

Priority

Dashboard

10 widgets

HIGH

Surge Hub

12 AI widgets

HIGH

Team Show

5 sections

MEDIUM

Player Stats

4 tabs

MEDIUM

Roster Show

3 tabs

MEDIUM


Phase 5: New Features to Implement

5.1 Lazy Loading Components

{{-- Lazy load heavy components --}}


{{-- With placeholder --}}

5.2 Teleport

For modals and overlays that need to render at document root:

{{-- Teleport modal to body to avoid z-index issues --}}

@teleport('body')

@endteleport

5.3 Form Objects (New in Livewire 4)

Create dedicated form classes for complex forms:

// app/Livewire/Forms/PlayerForm.php

namespace App\Livewire\Forms;

use Livewire\Form;

class PlayerForm extends Form

{

public string $riot_id = '';

public string $role = '';

protected function rules(): array

{

return [

'riot_id' => 'required

string

regex:/^.+#.+$/',

'role' => 'required

in:top,jungle,mid,adc,support',

];

}

protected function messages(): array

{

return [

'riot_id.regex' => 'Please enter a valid Riot ID (e.g., Faker#KR1)',

];

}

}

// Usage in component

class AddPlayer extends Component

{

public PlayerForm $form;

public function save()

{

$this->form->validate();

// Process $this->form->riot_id, $this->form->role

}

}

5.4 Wire Navigate Improvements

{{-- Prefetch on hover for faster navigation --}}

View Profile

{{-- Prefetch all links --}}


Phase 6: Testing Strategy

6.1 Test Updates Required

Update existing Livewire tests to use new testing methods:

// Before

Livewire::test(Dashboard::class)

->assertSet('user', $user);

// After (Livewire 4 - same API, verify compatibility)

Livewire::test(Dashboard::class)

->assertSet('user', $user);

6.2 New Test Patterns

// Test loading states

Livewire::test(FetchGame::class, ['game' => $game])

->call('searchGame')

->assertDispatched('notify');

// Test islands independently

Livewire::test(PodcastWidget::class)

->call('requestGeneration')

->assertSet('optimisticGenerating', true);

6.3 Run Test Suite

Run all Livewire-related tests

php artisan test --filter=Livewire

Run specific component tests

php artisan test tests/Feature/Livewire/


Phase 7: Implementation Checklist

Week 1: Foundation

  • [ ] Update composer.json to require Livewire 4
  • [ ] Run composer update livewire/livewire
  • [ ] Verify Flux UI compatibility
  • [ ] Run test suite to identify breaking changes
  • [ ] Fix any immediate compatibility issues

Week 2: Listener Migration

  • [ ] Migrate Dashboard.php listeners to #[On()]
  • [ ] Migrate Roster/Show.php listeners
  • [ ] Migrate Team/Show.php listeners
  • [ ] Migrate Stats/Show.php listeners
  • [ ] Migrate remaining 10 components with $listeners

Week 3: Loading States Enhancement

  • [ ] Update fetch-game.blade.php with new loading patterns
  • [ ] Update roster/show.blade.php loading states
  • [ ] Update podcast-widget.blade.php loading states
  • [ ] Add loading delays to prevent flash
  • [ ] Implement skeleton loaders where appropriate

Week 4: Transitions & Animations

  • [ ] Replace x-transition with wire:transition where applicable
  • [ ] Add wire:transition to notification components
  • [ ] Add wire:transition to modal components
  • [ ] Add wire:transition to dashboard widgets
  • [ ] Remove redundant Alpine transition code

Week 5: Islands Implementation

  • [ ] Convert Dashboard to use Islands
  • [ ] Convert Surge Hub to use Islands
  • [ ] Convert Team Show page to use Islands
  • [ ] Performance testing and optimization
  • [ ] Document island patterns for team

Week 6: New Features & Polish

  • [ ] Implement lazy loading for heavy components
  • [ ] Add Form Objects for complex forms
  • [ ] Implement wire:navigate prefetching
  • [ ] Final testing and QA
  • [ ] Update documentation

Breaking Changes Quick Reference

Change

Status

Notes

$listeners array deprecated

⚠️ UPDATE

Use #[On()] attribute

Property visibility

βœ… CHECK

Ensure public for wire:model

Alpine.js bundled

βœ… CHECK

Remove manual Alpine imports

Event names

βœ… CHECK

Use kebab-case

wire:model modifiers

βœ… CHECK

.live, .blur, .debounce


Resources


Rollback Plan

If critical issues are discovered:

1. Revert composer.json to livewire/livewire:^3.0

2. Run composer update livewire/livewire

3. Test critical paths

4. Document issues for future attempt


Last Updated: January 2026 Author: Claude (AI Assistant)

Advertisement