Install
$ agentstack add skill-pekral-cursor-rules-frontend-patterns ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Constraints
- Apply
@rules/laravel/livewire.mdc— class inapp/Livewire, view inresources/views/livewire, extendLivewire\Component; components are slim entry points; delegate business logic to Actions/Services; inject dependencies viaboot(), never as method params; Blade stays presentation-only. - Apply
@rules/laravel/filament.mdc— prefer Filament form/table components for admin UIs; custom Blade+Tailwind needs a registered theme. - Apply
@rules/laravel/architecture.mdc— keep query/business logic out of views and components. - Apply
@rules/sql/optimalize.mdc— eager-load to avoid N+1 in loops rendered by Blade. - Stack is Blade + Livewire + Alpine.js + Filament + Tailwind. No React/Vue/Next — never output
useState/useEffect/useMemo/JSX/Framer Motion/React Query.
Use when
- Composing UI from Blade/Livewire components.
- Deciding where state lives (Livewire vs Alpine).
- Optimizing render/network cost of a Livewire view.
- Building forms with validation.
- Handling loading, empty, error, and offline states.
Component composition
Compose; do not inherit. Prefer small components with slots over big configurable ones.
{{-- resources/views/components/card.blade.php (anonymous component) --}}
@props(['variant' => 'default'])
class(['card', 'card-outlined' => $variant === 'outlined']) }}>
{{ $slot }}
Title
Content
- Anonymous components (view-only, in
resources/views/components) for pure presentation. - Class components (
app/View/Components) only when the component needs PHP logic to prepare data. {{ $attributes }}forwards caller classes/attributes — merge, don't overwrite.- Named slots (``) replace prop-drilling content.
Livewire nesting
A "compound" UI is a parent Livewire component holding child Livewire components. Children are independent; pass data down via props and communicate up via events/listeners — never tight coupling.
@foreach ($rows as $row)
id" />
@endforeach
Always set :key on nested components and loop items so Livewire tracks identity across re-renders.
State placement: Livewire vs Alpine
Put state where it belongs. The wrong choice causes either chattiness or lost server state.
- Livewire public properties — server-authoritative data, anything persisted or validated, anything other components react to.
- Livewire computed properties (
#[Computed]) — derived values from properties/DB; cached per request, keeps the view clean. - Alpine
x-data— purely local UI state that the server never needs: dropdown open/closed, active tab, hover, optimistic toggles.
{{-- local-only: no server round-trip --}}
Menu
…
// derived server state
#[Computed]
public function total(): int
{
return $this->items->sum('price');
}
Rule of thumb: if toggling it should hit the database or affect validation, it is Livewire; if it is ephemeral chrome, it is Alpine. Bridge the two with @entangle only when both sides genuinely need the value.
Performance
wire:keyin loops — mandatory; without it Livewire mis-reconciles DOM and loses focus/state.- Tune
wire:model— default is deferred (syncs on action). Use.liveonly when the server must react to every keystroke; prefer.bluror.debounce.500msfor inputs to cut requests.
- Lazy / deferred loading — render expensive components after first paint with `
, or#[Lazy]` on the class, to keep the initial response fast. - Pagination — use
WithPagination; never load full tables into a property. - Avoid N+1 in views — eager-load relations in the query before passing to Blade; cross-reference
@rules/sql/optimalize.mdc. A relation accessed inside a@foreachwithout eager loading fires one query per row.
$this->orders = Order::with('customer')->latest()->paginate(20);
- Asset stacks — register component CSS/JS once with
@once+@push('scripts')so repeated components don't duplicate output.
Forms
Prefer Livewire form objects to keep components slim and validation reusable.
// app/Livewire/Forms/MarketForm.php
class MarketForm extends Form
{
#[Validate('required|string|max:200')]
public string $name = '';
#[Validate('required|string')]
public string $description = '';
}
// component
public MarketForm $form;
public function save(CreateMarket $action): void // Action injected via boot() or method DI
{
$this->validate();
$action->handle($this->form->toArray());
$this->reset('form');
}
- Real-time validation —
wire:model.blurplus anupdated()hook (or per-field$this->validateOnly($field)) validates as the user leaves each field without validating the whole form on every key. - Keep
messages()/attributes()free of identity-revealing detail per@rules/security/frontend.md. - For admin CRUD, prefer Filament forms over hand-built ones.
Loading, empty, error, offline states
Design all four states, not just the happy path.
{{-- loading --}}
Save
Saving…
{{-- empty --}}
@forelse ($orders as $order)
id" />
@empty
@endforelse
{{-- offline --}}
You are offline — changes will retry.
- Errors — surface failures with
session()->flash()+ arole="alert"region, or a validation message; never swallow exceptions in the component. Delegate the actual work to an Action that can throw, and catch only to present a safe message. - Skeletons — show
wire:loadingplaceholders for lazy/deferred components so layout doesn't jump.
Progressive enhancement with Alpine
Render meaningful HTML server-side first; layer Alpine for interactivity so the page is useful before JS runs and degrades gracefully if it doesn't.
{{-- works without JS via native ; Alpine enhances --}}
Filters
…
Keep Alpine logic small and inline; if it grows beyond a few expressions, move the state into a Livewire component.
Done when
- Components are composed from slots/attributes; no oversized configurable mega-components.
- State sits on the correct layer (Livewire for server/validated, Alpine for local chrome).
- Every loop and nested component has a stable
wire:key/:key. wire:modelstrategy minimizes requests; no.livewhere.blur/.debouncesuffices.- Lists eager-load relations — no N+1 in Blade.
- Forms use form objects + validation; admin CRUD reuses Filament.
- Loading, empty, error, and offline states are all handled.
- No React/Vue artifacts; business logic stays in Actions/Services, not the component.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: pekral
- Source: pekral/cursor-rules
- License: MIT
- Homepage: https://pekral.cz
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.