abduazam's avatar
Level 15

Best Practices for Using Livewire in Modular Laravel Applications?

I build Laravel applications using Livewire, following a modular structure. I organize my projects by creating a modules folder, where each module contains its models, migrations, routes, views, and even Livewire classes and views as well.

Since I have developed several applications using this approach, I have a question: Is my method correct, or is there a better way to structure Livewire components in a modular Laravel app? My projects work well, but I am curious to learn about best practices and potential improvements.

To manage views, Blade components, and Livewire components within each module, I have created a ViewServiceProvider for each module:

final class ViewServiceProvider extends AbstractViewServiceProvider
{    
    protected string $namespace = 'auth';    
    protected string $wireNamespace = 'authable';    
    
    // Livewire components    
    protected array $components = [
        'login' => Login::class,
        'register' => Register::class,
    ];
    
    public function boot(): void
    {        
        $this->loadViewsFrom(__DIR__ . '/../Views/view', $this->namespace);        
        $this->loadViewsFrom(__DIR__ . '/../Views/livewire', $this->wireNamespace);        
        
        Blade::anonymousComponentPath(__DIR__ . '/../Views/components', $this->namespace);        
        
        $this->loadLivewireViews($this->namespace, $this->components);    
    }
}

I also have a Controller to manage routes and render view blades:

web.php

Route::middleware('guest')->group(function () {    
    Route::get('login', [LoginController::class, 'create'])->name('login');
});

LoginController.php

final class LoginController extends Controller
{    
    public function create(): View    
    {        
        return view('auth::login');
    }
}

Login.php (Livewire Component)

final class Login extends Component
{    
    public LoginForm $form;

    public function login(): void
    {
        $this->form->validate();

        // Login logic (same as Laravel Livewire Starter Kit)

        $this->redirectIntended(route('dashboard.home', absolute: false));
    }

    public function render(): View
    {
        return view('authable::login');
    }
}

Folder Structure

Within the Views folder, I organize files as follows:

    - view  
        - login.blade.php  # Rendered by the controller
    - livewire   
        - login.blade.php  # Rendered by the Livewire component

View login blade (view/login.blade.php)

<x-layouts.auth-layout>
    <div class="w-80 max-w-80 space-y-6">
        <div class="flex justify-center opacity-50">
            <x-logo.app />
        </div>

        <div class="block">
            <flux:heading class="text-center mb-3" size="xl">Log in to your account</flux:heading>

            <flux:text class="text-center">Enter your email and password below to log in</flux:text>
        </div>

        <livewire:auth.login />

        <flux:subheading class="text-center">
            Don't have an account? <flux:link href="{{ route('register') }}">Sign up for free</flux:link>
        </flux:subheading>
    </div>
</x-layouts.auth-layout>

Livewire login blade (livewire/login.blade.php)

<form wire:submit="login" class="flex flex-col gap-6">
    <flux:input wire:model="form.email" label="Email address" type="email" autofocus required autocomplete="email" placeholder="[email protected]" />

    <flux:field>
        <div class="mb-3 flex justify-between">
            <flux:label>Password</flux:label>

            <flux:link href="{{ route('password.request') }}" variant="subtle" class="text-sm">Forgot password?</flux:link>
        </div>

        <flux:input wire:model="form.password" type="password" placeholder="Your password" required autocomplete="current-password" />
    </flux:field>

    <flux:checkbox wire:model="form.remember" label="Remember me for 20 days" />

    <div class="flex items-center justify-end">
        <flux:button variant="primary" type="submit" class="w-full">Log in</flux:button>
    </div>
</form>

Is this a proper way to structure Livewire in a modular Laravel application? Are there any best practices I should follow or improvements I can make?

0 likes
2 replies
LaryAI's avatar
Level 58

Your approach to structuring a modular Laravel application with Livewire components is quite organized and follows a logical pattern. Here are some best practices and potential improvements you might consider:

  1. Namespace Organization:

    • Ensure that your namespaces are consistent and reflect the folder structure. This helps in maintaining clarity and avoiding conflicts.
  2. Service Providers:

    • Using a ViewServiceProvider for each module is a good practice. It allows you to encapsulate the logic related to views and Livewire components within the module itself.
    • Consider using the register method in your service provider to bind any services or perform actions that should occur during the application's bootstrapping process.
  3. Livewire Components:

    • Ensure that your Livewire components are named clearly and consistently. This helps in understanding their purpose at a glance.
    • Consider using Livewire's mount method for initializing component state, which can make your components more predictable and easier to test.
  4. Blade Views:

    • Your separation of views into view and livewire directories is a good practice. It keeps the concerns of static and dynamic views separate.
    • Ensure that your Blade components are reusable and follow a consistent naming convention.
  5. Routing:

    • Grouping routes by middleware and using controllers to handle requests is a standard practice. It keeps your route files clean and manageable.
    • Consider using route model binding where applicable to simplify your controller logic.
  6. Configuration and Environment:

    • If your modules have specific configuration needs, consider creating configuration files for each module and loading them in the service provider.
    • Ensure that any environment-specific settings are managed through environment variables.
  7. Testing:

    • Implement tests for your Livewire components and routes to ensure they behave as expected. This can be done using Laravel's built-in testing tools.
  8. Documentation:

    • Maintain documentation for each module, especially if they are intended to be reused across different projects. This can include setup instructions, dependencies, and usage examples.
  9. Performance Considerations:

    • Monitor the performance of your Livewire components, especially if they are handling large datasets or complex interactions. Consider using pagination or lazy loading where applicable.
  10. Code Quality:

    • Regularly review and refactor your code to adhere to SOLID principles and Laravel's best practices. This includes using dependency injection, avoiding tight coupling, and ensuring single responsibility.

Overall, your current structure is well thought out and aligns with modular design principles. By following these best practices, you can further enhance the maintainability and scalability of your Laravel applications.

abduazam's avatar
Level 15

I also have some ideas for changes:

Separating Business Logic from Livewire Components

I don't want to handle business logic inside Livewire components. Instead, I want to extract the logic into services, actions, or controllers while keeping Livewire components focused purely on reactivity.

Binding Livewire Components Directly to Routes

Instead of using controllers, I could bind Livewire components directly to routes. I have tried this approach, and it works well.

This structure keeps the same modular approach while allowing me to make my app function more like a Single Page Application (SPA).

Please or to participate in this conversation.