laravel-nova

Auto-invoked skill

Build admin panels with Laravel Nova resources, actions, filters, lenses, and metrics

Trigger Keywords

This skill automatically activates when Claude detects these keywords:

nova nova resource nova action nova filter nova lens nova metric admin dashboard

Overview

The laravel-nova skill provides expertise for building premium admin panels with Laravel Nova. It covers resources, actions, filters, lenses, metrics, and custom tools.

License Required

Laravel Nova is a paid product requiring a license ($99/site or $199/developer). Purchase at nova.laravel.com

What This Skill Provides

  • Resources - Complete CRUD interfaces with fields and relationships
  • Actions - Standalone, bulk, and queued actions
  • Filters - Select, date, and boolean filters
  • Lenses - Custom views with specialized queries
  • Metrics - Value, Trend, and Partition metrics
  • Custom Tools - Extended functionality and integrations
  • Authorization - Fine-grained access control

Nova vs Filament

Choose Nova When:

  • Budget allows ($99/site)
  • Need official Laravel package
  • Want guaranteed support
  • Enterprise stability required

Choose Filament When:

  • Budget limited (free/open-source)
  • Need rapid development
  • Want modern TALL stack
  • More customization flexibility

Example Conversations

# Creating a Nova resource
"Create a Nova resource for managing products"

# Adding actions
"Add a bulk action to approve selected orders"

# Creating metrics
"Add a trend metric showing revenue over time"

# Building custom lenses
"Create a lens showing high-value customers"

Installation

# Configure Nova repository (requires license)
composer config repositories.nova composer https://nova.laravel.com

# Install Nova
composer require laravel/nova

# Run installer
php artisan nova:install

# Migrate database
php artisan migrate

# Create first Nova user
php artisan nova:user

Resource Example

<?php

declare(strict_types=1);

namespace App\Nova;

use Laravel\Nova\Fields\{ID, Text, Currency, BelongsTo, Select, Boolean, Markdown};
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Resource;

final class Product extends Resource
{
    public static string $model = \App\Models\Product::class;
    public static $title = 'name';
    public static $search = ['id', 'name', 'sku'];
    public static $group = 'Catalog';

    public function fields(NovaRequest $request): array
    {
        return [
            ID::make()->sortable(),

            Text::make('Name')
                ->sortable()
                ->rules('required', 'max:255'),

            Text::make('SKU')
                ->rules('required', 'max:50')
                ->creationRules('unique:products,sku')
                ->updateRules('unique:products,sku,'),

            Currency::make('Price')
                ->currency('USD')
                ->rules('required', 'numeric', 'min:0'),

            BelongsTo::make('Category')
                ->searchable()
                ->withSubtitles(),

            Select::make('Status')
                ->options([
                    'draft' => 'Draft',
                    'published' => 'Published',
                ])
                ->displayUsingLabels(),

            Boolean::make('Featured')->default(false),

            Markdown::make('Description')->alwaysShow(),
        ];
    }
}

Actions

<?php

namespace App\Nova\Actions;

use Laravel\Nova\Actions\Action;
use Laravel\Nova\Fields\{ActionFields, Select, Textarea};
use Laravel\Nova\Http\Requests\NovaRequest;

final class UpdateOrderStatus extends Action
{
    public function handle(ActionFields $fields, Collection $models)
    {
        foreach ($models as $model) {
            $model->update([
                'status' => $fields->status,
                'notes' => $fields->notes,
            ]);
        }

        return Action::message('Status updated successfully!');
    }

    public function fields(NovaRequest $request): array
    {
        return [
            Select::make('Status')
                ->options([
                    'pending' => 'Pending',
                    'processing' => 'Processing',
                    'completed' => 'Completed',
                ])
                ->rules('required'),

            Textarea::make('Notes'),
        ];
    }
}

Filters

<?php

namespace App\Nova\Filters;

use Laravel\Nova\Filters\Filter;

final class OrderStatus extends Filter
{
    public function apply(Request $request, $query, $value)
    {
        return $query->where('status', $value);
    }

    public function options(Request $request): array
    {
        return [
            'Pending' => 'pending',
            'Processing' => 'processing',
            'Completed' => 'completed',
        ];
    }
}

Metrics

Value Metric

Display single values like totals, counts, averages

Trend Metric

Show data over time with line charts

Partition Metric

Display data distribution with pie charts

<?php

namespace App\Nova\Metrics;

use Laravel\Nova\Metrics\Value;

final class TotalRevenue extends Value
{
    public function calculate(NovaRequest $request)
    {
        return $this->sum($request, Order::class, 'total')
            ->currency('USD')
            ->format('0,0.00');
    }

    public function ranges(): array
    {
        return [
            30 => '30 Days',
            60 => '60 Days',
            90 => '90 Days',
        ];
    }
}

Common Pitfalls

Missing License Configuration

Must configure Nova composer repository before installation.

composer config repositories.nova composer https://nova.laravel.com

Not Publishing Assets

Assets must be published after updates.

php artisan nova:publish

Missing Authorization

Always implement authorization methods in resources.

public static function authorizedToViewAny(Request $request): bool
{
    return $request->user()->can('view-products');
}

N+1 Query Problems

Use eager loading to prevent performance issues.

public static $with = ['category', 'variants'];

Incorrect Validation Rules

Resources need special validation for unique fields.

Text::make('Email')
    ->creationRules('unique:users,email')
    ->updateRules('unique:users,email,')

Best Practices

  • Resource Organization - Group related resources using $group property
  • Performance - Always eager load relationships with $with
  • User Experience - Provide helpful field descriptions and validation messages
  • Security - Implement authorization methods on all resources
  • Code Quality - Use strict types and final classes

Package Integration

Official Packages

  • laravel/nova-log-viewer
  • laravel/nova-dusk-suite
  • spatie/nova-backup-tool

Third-Party

  • vyuldashev/nova-permission
  • alexbowers/nova-inline-select
  • davidpiesse/nova-map

Testing Nova Features

<?php

use App\Models\{Product, User};

describe('Nova Product Resource', function () {
    beforeEach(function () {
        $this->admin = User::factory()->admin()->create();
    });

    it('displays products in index', function () {
        $this->actingAs($this->admin)
            ->get('/nova-api/products')
            ->assertOk();
    });

    it('creates product', function () {
        $this->actingAs($this->admin)
            ->post('/nova-api/products', [
                'name' => 'New Product',
                'sku' => 'TEST-001',
                'price' => 99.99,
            ])
            ->assertCreated();
    });
});

Related Commands

php artisan nova:resource

Create Nova resource

php artisan nova:action

Create Nova action

php artisan nova:filter

Create Nova filter

php artisan nova:metric

Create Nova metric

Related Agents

Related Skills

Full Documentation

For complete examples and advanced patterns, see the full SKILL.md file.