Cmd+Click your way through Laravel projects
A community extension β not affiliated with Laravel LLC
Search "Laravel" in Zed Extensions and click Install.
From source: Clone the repo, run cargo build --release in laravel-lsp/, then use "zed: install dev extension".
The extension works out of the box with zero configuration. It automatically discovers your Laravel project structure, including view paths, component namespaces, route files, and service providers.
Optional settings can be added to your Zed settings.json:
{
"lsp": {
"laravel-lsp": {
"settings": {
"autoCompleteDebounce": 200,
"blade": {
"directiveSpacing": false
}
}
}
}
}| Setting | Default | Description |
|---|---|---|
autoCompleteDebounce |
200 |
Delay (ms) before autocomplete updates after typing. Lower values (50-100ms) give faster feedback. Higher values (300-500ms) reduce CPU usage. |
blade.directiveSpacing |
false |
Add space between directive name and parentheses. false: @if($condition) / true: @if ($condition) |
ποΈ Database autocomplete (exists:, unique: rules, Eloquent properties) requires a working database connection. Configure in your .env:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=secretSupports MySQL, PostgreSQL, SQLite, and SQL Server.
Navigate your Laravel codebase by Cmd+Clicking (or Cmd+D) on any recognized pattern. The extension understands Laravel's conventions and jumps directly to the source file, whether it's a view, component, route, config key, or translation.
class UserController extends Controller
{
public function show(User $user)
{
return view('users.profile', compact('user'));
// ^^^^^^^^^^^^^^^ β resources/views/users/profile.blade.php
}
}@extends('layouts.app')
{{-- ^^^^^^^^^^^ β resources/views/layouts/app.blade.php --}}
<x-button type="submit">Save</x-button>
{{-- ^^^^ β resources/views/components/button.blade.php --}}
<livewire:user-settings :user="$user" />
{{-- ^^^^^^^^^^^^^ β app/Livewire/UserSettings.php --}}$url = route('users.show', $user);
// ^^^^^^^^^^^^ β routes/web.php
$name = config('app.name');
// ^^^^^^^^^^ β config/app.php
$message = __('auth.failed');
// ^^^^^^^^^^^^ β lang/en/auth.phpSupported patterns:
view() View::make() @extends @include @component <x-*> </x-*> <livewire:*> </livewire:*> @livewire() route() to_route() config() Config::get() env() __() trans() @lang ->middleware() app() resolve() asset() @vite app_path() base_path() storage_path() resource_path() public_path() Feature::active() Feature::inactive() Feature::value() @feature
Get intelligent suggestions as you type. The extension provides context-aware completions for views, Blade components, validation rules, Eloquent casts, database schemas, config keys, routes, middleware, translations, environment variables, Eloquent models, and Blade variables.
$request->validate([
'email' => 'required|email|exists:',
// ^ ποΈ database tables appear here
'email' => 'required|email|exists:users,',
// ^ ποΈ column names appear here
'name' => 'required|',
// ^ π 90+ validation rules appear here
]);
$name = config('app.');
// ^ βοΈ config keys with resolved values
return view('users.');
// ^ π view names from resources/views
$url = route('users.');
// ^ π named routes from routes/*.php
Route::middleware('');
// ^ π‘οΈ middleware aliases from bootstrap/app.php
$message = __('auth.');
// ^ π translation keys with valuesGet autocomplete for Eloquent cast types in $casts property or casts() method:
protected $casts = [
'is_admin' => '',
// ^ π cast types appear here
];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'settings' => '',
// ^ string, integer, boolean, datetime, array,
// encrypted, hashed, collection, object...
];
}Cast completions include:
- Primitives:
string,integer,float,boolean,array,object,collection - Dates:
datetime,date,timestamp,immutable_date,immutable_datetime - Security:
encrypted,encrypted:array,encrypted:collection,hashed - Numbers:
decimal:(with precision parameter) - Custom casts from
app/Casts/and installed packages
Type $user-> to get completions for model properties, including database columns, casts, accessors, and relationships:
$user->
// ^ name (string) β database column
// ^ email (string) β database column
// ^ email_verified_at (Carbon) β cast to datetime
// ^ is_admin (bool) β cast to boolean
// ^ full_name (string) β accessor
// ^ posts (Collection) β hasMany relationshipWorks with type-hinted variables, PHPDoc annotations, and static chains like User::find(1)->.
Type $ in Blade files to see all available variables passed to the view:
{{ $
{{-- ^ user (User) β from controller
^ posts (Collection) β from controller
^ title (string) β from @props --}}Variables are resolved from:
view('name', compact('user', 'posts'))view('name', ['user' => $user])view('name')->with('user', $user)view('name')->with(['user' => $user])@props(['title' => string])in Blade components- Livewire component public properties
Variables from loop directives are available only inside the loop block:
@foreach($users as $user)
{{ $user->name }} {{-- β
$user available here --}}
{{ $loop->index }} {{-- β
$loop available in all loops --}}
@endforeach
{{ $user }} {{-- β $user NOT available outside loop --}}Supported loop directives:
@foreach($items as $item)/@foreach($items as $key => $value)@forelse($items as $item)@for($i = 0; $i < 10; $i++)@while($condition)
Nested loops work correctlyβinner loop variables are scoped to their block.
In component files (resources/views/components/*.blade.php), slot variables are detected from usage:
{{-- components/card.blade.php --}}
<div class="card">
<header>{{ $header }}</header> {{-- $header autocomplete available --}}
<div>{{ $slot }}</div> {{-- $slot always available --}}
<footer>{{ $footer }}</footer> {{-- $footer autocomplete available --}}
</div>Component files automatically get:
$slotβ default slot content$attributesβ component attribute bag$componentβ component instance- Named slots detected from
{{ $name }}usage
Get autocomplete for Laravel Pennant feature flags in PHP and Blade:
Feature::active('');
// ^ π© feature names from app/Features/
Feature::for($user)->active('');
// ^ π© same completions for scoped checks
Feature::allAreActive(['']);
// ^ π© works in array methods too@feature('')
{{-- ^ π© feature names appear here --}}Features are discovered from app/Features/*.php class files. Both string keys ('new-api') and class references (NewApi::class) are supported.
See problems in real-time as you type. The extension validates your Laravel code against your actual project structure, highlighting missing views, undefined components, invalid validation rules, and other issues before you run your application.
Missing files are reported as errors to catch issues early:
return view('users.dashboard');
// ^^^^^^^^^^^^^^^^^ β View not found: resources/views/users/dashboard.blade.php
Route::middleware('admin-only')->group(...);
// ^^^^^^^^^^^^ β οΈ Middleware not found
$request->validate([
'email' => 'required|emal|unique:users',
// ^^^^ β Unknown validation rule: 'emal'
]);
Feature::active('undefined-feature');
// ^^^^^^^^^^^^^^^^^^ β Feature not found: app/Features/UndefinedFeature.php<x-dashboard-widget />
{{-- ^^^^^^^^^^^^^^^^ β Component not found --}}
<livewire:admin-panel />
{{-- ^^^^^^^^^^^ β Livewire component not found --}}
@extends('layouts.missing')
{{-- ^^^^^^^^^^^^^^^^ β View not found --}}
@feature('undefined-feature')
{{-- ^^^^^^^^^^^^^^^^^^ β Feature not found --}}Fix problems with a single click. When you see a warning, press Cmd+. to open quick actions. The extension offers to create missing files with the correct Laravel structureβviews, components, middleware, translations, and more.
return view('users.dashboard');
// ^^^^^^^^^^^^^^^^^ β οΈ View not found
// β‘ Create view: users.dashboard
Route::middleware('admin-only')->group(...);
// ^^^^^^^^^^^^ β οΈ Middleware not found
// β‘ Create middleware: admin-only<x-dashboard-widget />
{{-- β οΈ Component not found
β‘ Create component (anonymous)
β‘ Create component with class --}}
<livewire:admin-panel />
{{-- β οΈ Livewire component not found
β‘ Create Livewire component --}}Available quick actions:
- π Create missing views
- π§© Create Blade components (anonymous or with class)
- β‘ Create Livewire components
- π‘οΈ Create middleware
- π© Create Laravel Pennant feature classes
- π Add translations to existing files
- π Add environment variables to
.env
Full Blade template language support with syntax highlighting, smart completions, and editor integration.
Blade directives, PHP blocks, echo statements, and comments are all properly highlighted with distinct colors.
Type @ to see all 100+ Blade directives with descriptions:
@fo
β³ @foreach Loop through collection
β³ @for For loop
β³ @forelse Loop with empty fallbackBlock directives automatically include their closing tags:
@if($condition)
β
@endifType { and select from snippet completions:
{
β³ {{ ... }} Echo (escaped)
β³ {!! ... !!} Echo (unescaped)
β³ {{-- ... --}} Blade commentCursor is automatically positioned between the brackets for immediate typing.
Cmd+Click works on both opening AND closing tags:
<x-button>Submit</x-button>
{{-- ^^^^^^ ^^^^^^ Both navigate to component --}}
<livewire:counter></livewire:counter>
{{-- ^^^^^^^ ^^^^^^^ Both navigate to Livewire class --}}- π Hover documentation with resolved values
- π¨ Inertia.js support (
Inertia::render('Page')) - π Folio page routing
- β‘ Volt component support
zed-laravel/
βββ src/lib.rs # Zed extension (binary download/management)
βββ extension.toml # Extension manifest
βββ languages/ # Blade & PHP language definitions
β βββ blade/ # Syntax highlighting, injections, indents
β βββ php_only/ # PHP syntax support
βββ laravel-lsp/ # Laravel Language Server (the actual LSP)
β βββ src/main.rs # LSP server implementation
β βββ src/queries.rs # Tree-sitter pattern extraction
β βββ tests/ # Integration tests
βββ test-project/ # Laravel fixture for testing
-
Clone and build the LSP:
git clone https://github.com/GeneaLabs/zed-laravel.git cd zed-laravel/laravel-lsp cargo build --release -
Configure Zed to use your local build:
Add to your Zed
settings.json:{ "lsp": { "laravel-lsp": { "binary": { "path": "/path/to/zed-laravel/laravel-lsp/target/release/laravel-lsp" } } } } -
Install the extension for language support:
In Zed:
Cmd+Shift+Pβ "zed: install dev extension" β select thezed-laraveldirectory. -
After making changes:
cd laravel-lsp && cargo build --release
Then in Zed:
Cmd+Shift+Pβ "zed: reload extensions"
cd laravel-lsp
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_view_resolution# Format code
cargo fmt
# Run linter
cargo clippy