Blade/Livewire Coding Guidelines

This guide documents coding standards and best practices for Blade templates and Livewire components.

Blade Template Conventions

File Naming

Template Structure

{{-- Comments --}}
@extends('layouts.app')

@section('content')
    <div class="container">
        {{-- Template content --}}
    </div>
@endsection

Avoid Shorthand Notation

Critical: Do not use shorthand component notation as it can cause parsing issues.

<!-- Good: Full syntax -->
<x-slot name="header">Header Content</x-slot>

<!-- Bad: Shorthand -->
<x-slot:header>Header Content</x-slot:header>

Variable Usage in Components

Use variables instead of string interpolation to avoid nesting issues:

<!-- Good: Variable -->
@php($routePrefix = 'items')
<x-component :route="$routePrefix . '.create'" />

<!-- Bad: String interpolation in component -->
<x-component route="{{ $entity }}.create" />

Blade Directives

Control Structures

@if($condition)
    {{-- Content --}}
@elseif($otherCondition)
    {{-- Content --}}
@else
    {{-- Content --}}
@endif

@foreach($items as $item)
    {{ $item->name }}
@endforeach

@forelse($items as $item)
    {{ $item->name }}
@empty
    <p>No items found.</p>
@endforelse

Authentication

@auth
    {{-- User is authenticated --}}
@endauth

@guest
    {{-- User is not authenticated --}}
@endguest

Authorization

@can(\App\Enums\Permission::VIEW_DATA->value)
    <a href="{{ route('items.index') }}">View Items</a>
@endcan

@cannot(\App\Enums\Permission::DELETE_DATA->value)
    <p>You cannot delete items.</p>
@endcannot

PHP in Blade

Keep PHP blocks at the top of sections:

@section('content')
    @php($c = $entityColor('items'))
    @php($title = 'Items List')

    <div class="{{ $c['bg'] }}">
        <h1>{{ $title }}</h1>
    </div>
@endsection

Don’t nest @php blocks inside control structures:

<!-- Bad: PHP block inside @if -->
@if($condition)
    @php($variable = 'value')
    {{ $variable }}
@endif

<!-- Good: PHP block before control structure -->
@php($variable = $condition ? 'value' : 'default')
@if($condition)
    {{ $variable }}
@endif

Component Development

Component Properties

Document component props with @props:

@props([
    'entity' => '',
    'title' => '',
    'backRoute' => '',
])

Default Values

Provide sensible defaults:

@props([
    'size' => 'md',
    'variant' => 'primary',
    'entity' => null,
])

Required vs Optional

Use prop validation for required props:

@props([
    'entity',  // Required (no default)
    'title' => '',  // Optional (has default)
])

Slots

Use named slots for complex components:

<x-modal>
    <x-slot name="title">Modal Title</x-slot>
    <x-slot name="content">Modal Content</x-slot>
    <x-slot name="footer">Modal Footer</x-slot>
</x-modal>

Livewire Component Conventions

Component Structure

class ItemsTable extends Component
{
    use WithPagination;

    // Public properties (wire:model)
    public $sortField = 'created_at';
    public $sortDirection = 'desc';
    public $search = '';

    // Query string parameters
    protected $queryString = [
        'search' => ['except' => ''],
        'sortField' => ['except' => 'created_at'],
    ];

    // Methods
    public function sortBy($field)
    {
        // Logic
    }

    public function render()
    {
        return view('livewire.tables.items-table', [
            'items' => $this->getItems(),
        ]);
    }
}

Property Naming

Lifecycle Hooks

public function mount()
{
    // Called when component is initialized
}

public function updated($propertyName)
{
    // Called when any property is updated
}

public function updatedSearch()
{
    // Called when specific property is updated
    $this->resetPage();
}

Entity Color System

Always use the entity color helper:

@php($c = $entityColor('items'))

<div class="{{ $c['bg'] }}">Background</div>
<a href="#" class="{{ $c['accentLink'] }}">Link</a>
<button class="{{ $c['button'] }}">Button</button>

Available Color Properties

Styling Conventions

Tailwind Classes

Class Order Example

<div class="flex items-center justify-between p-4 bg-white rounded-lg shadow-sm hover:shadow-md">

Responsive Design

<div class="flex flex-col sm:flex-row gap-4">
    <!-- Mobile: stacked, Desktop: horizontal -->
</div>

<td class="hidden lg:table-cell px-4 py-3">
    <!-- Hidden on mobile, visible on desktop -->
</td>

Error Handling

Validation Errors

Display validation errors automatically with form components:

<x-form.field label="Name" name="internal_name">
    <x-form.input name="internal_name" :value="old('internal_name')" />
</x-form.field>

Flash Messages

Show success/error messages:

@if(session('status'))
    <x-ui.alert :message="session('status')" type="success" entity="items" />
@endif

Performance

Eager Loading

Prevent N+1 queries:

$items = Item::with(['partner', 'country', 'translations'])->get();

Pagination

Always paginate large datasets:

$items = Item::paginate(15);

Livewire Lazy Loading

<livewire:items-table lazy />

Security

Mass Assignment Protection

Define fillable or guarded in models:

protected $fillable = ['internal_name', 'type'];

CSRF Protection

Always include CSRF token in forms:

<form method="POST">
    @csrf
</form>

Authorization

Check permissions in views:

@can(\App\Enums\Permission::CREATE_DATA->value)
    <a href="{{ route('items.create') }}">Add Item</a>
@endcan

Best Practices

  1. DRY Principle - Extract reusable components and partials
  2. Entity Colors - Use color system for consistency
  3. Responsive Design - Mobile-first approach
  4. Accessibility - Semantic HTML, ARIA labels, keyboard navigation
  5. Performance - Eager loading, pagination, caching
  6. Security - CSRF tokens, authorization, validation
  7. Testing - Write tests for components and workflows
  8. Documentation - Comment complex logic