Skip to content
Go back

I Asked AI to Audit My Page Speed. It Found 9 Issues.

My app felt slow loading individual idea pages. Not broken — just sluggish. The kind of thing where you notice the delay but wouldn’t know where to start looking.

So I asked one question:

“the app feels a little slow when loading an individual idea, is there anything obvious in the code that could be having a negative impact on performance?”

Claude Code scanned the page’s code — the Livewire component, the Blade template, the models, and their relationships — and came back with 9 issues, prioritised by severity. I didn’t need to know what to look for. I just needed to ask.

If your Laravel app feels slow, try the same prompt on your slowest page. Here’s what it found on mine.

Table of contents

Open Table of contents

The prompt that finds performance issues

Before getting into the fixes, this is the important part. You don’t need to be a performance expert. You don’t need to know about N+1 queries or Livewire dehydration cycles. You just need to ask:

“This page feels slow — is there anything obvious in the code that could be having a negative impact on performance?”

Paste a URL or point to the component file. The AI will read the code, trace the data flow, and identify bottlenecks you’d never think to look for. The 9 issues below were all found from that single question.

Critical: hidden component making API calls on every page load

The idea edit page had a slide-over panel for AI-powered hypothesis building. The panel was hidden by default — but the Livewire component behind it still mounted on every page load.

// This runs on EVERY page load, even when the panel is hidden
public function mount(): void
{
    $conversation = Conversation::query()->create([...]);
    $this->js('$nextTick(() => $wire.streamResponse())');
}

Every time someone opened an idea, the app was creating a database record and firing an API call to OpenAI — for a feature the user hadn’t opened. This was the single biggest performance hit.

The fix

Don’t mount the component until the user actually opens the panel. Alpine’s x-if directive only renders the component when the condition is true:

<!-- Before: component mounts immediately, even when hidden -->
<livewire:hypothesis-ai-chat />

<!-- After: component only mounts when panel is opened -->
<template x-if="open">
    <livewire:hypothesis-ai-chat />
</template>

The lesson: Livewire components inside hidden modals, slide-overs, and panels still mount and run their logic. If a component makes API calls or database queries on mount, it’s doing that work on every page load whether the user sees it or not.

Critical: model $appends triggering HTTP requests on serialization

The Goal model had appended attributes that fetched external data:

protected $appends = [
    'last_month_data',        // Makes an HTTP request
    'previous_28_days_data',  // Makes an HTTP request
    'statusColor',
];

Every time a Goal was serialized — toArray(), JSON encoding, or Livewire’s dehydration cycle — these attributes triggered up to 3 external HTTP requests. And Goals were being serialized multiple times per page load across different components.

The lesson: $appends on Eloquent models run every time the model is serialized. If an appended attribute makes an API call or runs an expensive query, that cost multiplies every time the model passes through Livewire’s render cycle.

High: expensive query in render() instead of mount()

The component’s render() method was querying all idea IDs to calculate previous/next navigation:

// This runs on EVERY Livewire re-render, not just the first load
public function render()
{
    $allIdeaIds = (clone $baseQuery)->pluck('id')->toArray();
    $currentPosition = array_search($this->idea->id, $allIdeaIds);
    // ...
}

In Livewire, render() runs on every update — every form field change, every button click, every poll interval. Expensive queries here multiply fast.

The lesson: Put expensive queries in mount() (runs once) rather than render() (runs on every update). If the data doesn’t change between updates, it shouldn’t be recalculated.

High: serializing an entire model unnecessarily

The Like component received a full Idea model and immediately called toArray():

public function mount(Idea $idea)
{
    $this->record = $idea->toArray();
}

This serialized the entire Idea — including its Goal relationship, which triggered the $appends HTTP requests described above. The Like component only needed the idea’s ID and whether the current user had liked it.

The lesson: Don’t pass entire models to components that only need a few fields. Pass just the data you need.

Medium: missing eager-loading (N+1 queries)

The template accessed $idea->author without eager-loading the relationship. Each idea loaded its author with a separate query — the classic N+1 problem.

The lesson: If your template accesses a relationship (like $idea->author->name), make sure it’s eager-loaded with ->with('author') in the query.

Medium: comments loading in a hidden modal

The comments component queried all comments on mount, even though comments were inside a modal that the user might never open.

The lesson: Same pattern as the AI chat — hidden modals still mount their Livewire components. Either lazy-load with x-if, use Livewire’s #[Lazy] attribute, or move the query to a method that only runs when the modal opens.

The full list

#IssueSeverityRoot cause
1AI chat mounting on every page loadCriticalHidden component still mounts
2Model $appends making HTTP requestsCriticalSerialization triggers API calls
3Query in render() instead of mount()HighRuns on every Livewire update
4Full model serialization in Like componentHightoArray() cascades through relationships
5Missing author eager-loadMediumN+1 query
6Comments loading in hidden modalMediumHidden component still mounts
7All goals loaded as public propertyLowPotential $appends trigger
8wire:poll.2s compounding render costLowPolls trigger full re-renders
9Team members loaded eagerlyLowComputed property loads all users

Key takeaway

You don’t need to be a performance expert to find and fix slow pages. Ask your AI assistant one question — “this page feels slow, what’s obvious?” — and let it trace the code for you. The most common patterns: components that mount when they shouldn’t, models that trigger expensive operations on serialization, and queries that run on every update instead of once on load.


Back to top ↑