spatie-guidelines
Spatie's coding guidelines and conventions. Use when writing PHP, Laravel, JavaScript, or Vue code for Spatie projects or packages. Covers code style, naming, routing, controllers, Blade, validation, Git workflow, package structure, testing (Pest), and service providers. Triggers include "follow Spatie guidelines", "Spatie style", "Spatie package", or any code review for Spatie packages/projects.
What this skill does
# Spatie Guidelines
Apply these guidelines when writing code for Spatie projects or contributing to Spatie packages.
**Core principle:** Write things the way Laravel intended. If there's a documented way, follow it. Deviate only with justification.
---
## PHP Style
### Type System
- Type properties, parameters, and return types. Skip docblocks for fully typed methods.
- Use `?Type` (short nullable), not `Type|null`.
- Use `void` return type when a method returns nothing.
- Use constructor property promotion when all properties can be promoted; one per line, trailing comma:
```php
class MyClass {
public function __construct(
protected string $firstArgument,
protected string $secondArgument,
) {}
}
```
### Docblocks
- Skip docblocks for fully type-hinted methods unless you need a description.
- Use full sentences with a period for descriptions.
- Always import classnames in docblocks (use FQCNs like `\Spatie\Url\Url`).
- One-line docblocks when possible: `/** @var string */`
- Always add docblock types for iterables: `@param array<int, string> $items`
- If a function needs one docblock param, add all other params too.
### Code Style
- PSR-1, PSR-2, PSR-12.
- Don't use `final` by default.
- Prefer string interpolation: `"Hi, I am {$name}."`
- Enums use PascalCase values: `case Diamonds;`
- Each trait on its own line with its own `use`:
```php
class MyClass
{
use TraitA;
use TraitB;
}
```
### Control Flow
- **Happy path last** — handle failures first, return early.
- **Avoid `else`** — refactor to early returns or ternaries.
- **Separate compound ifs** — individual `if` statements over `&&` chains.
- Always use curly brackets for `if` statements.
- Whitespace: blank lines between statements (except sequences of single-line operations).
- No extra empty lines between `{}` brackets.
### Comments
- Avoid comments. Write expressive code instead.
- Refactor comments into descriptively named methods.
---
## Laravel Conventions
### Configuration
- Config filenames: **kebab-case** (`media-library.php`, `permission.php`)
- Config keys: **snake_case** (`'chrome_path' => env('CHROME_PATH')`)
- Never use `env()` outside config files.
- Service-specific config goes in `config/services.php`, not a new file.
### Routing
- URLs: **kebab-case** (`/open-source`, `/front-end-developer`)
- Route names: **camelCase** (`->name('openSource')`)
- Route parameters: **camelCase** (`{newsItem}`)
- HTTP verb first: `Route::get('open-source', [OpenSourceController::class, 'index'])`
- Use tuple notation: `[Controller::class, 'method']`, not string `'Controller@method'`
- Don't prefix URLs with `/` (except root `/`)
### API Routing
- Plural resource names: `/errors`, `/error-occurrences`
- Kebab-case resources
- Limit deep nesting. Prefer `/error-occurrences/1` over `/projects/1/errors/1/error-occurrences/1`
- Nest only when context is necessary: `/errors/1/occurrences`
### Controllers
- **Plural** resource name + `Controller` suffix: `PostsController`
- Stick to CRUD keywords: `index`, `create`, `store`, `show`, `edit`, `update`, `destroy`
- Extract new controllers for non-CRUD actions (e.g., `FavoritePostsController` with `store`/`destroy`)
- Invokable controllers for single actions: `PerformCleanupController`
### Views & Blade
- View files: **camelCase** (`openSource.blade.php`)
- Indent with 4 spaces.
- No spaces after directives: `@if($condition)`
- Use `__()` for translations, not `@lang`
### Validation
- Always array notation: `['required', 'email']`, never pipe `'required|email'`
- Custom rules: **snake_case** (`organisation_type`)
### Authorization
- Policies: **camelCase** (`editPost`)
- Use CRUD words; replace `show` with `view`
### Artisan Commands
- Command names: **kebab-case** (`delete-old-records`)
- Always output feedback. Minimum: `$this->comment('All ok!')` at end.
- For batch processing: output progress per item, summary at end.
### Naming Classes
| Type | Convention | Example |
|------|-----------|---------|
| Controller | Plural + `Controller` | `PostsController` |
| Invokable Controller | Action + `Controller` | `PerformCleanupController` |
| Model | Singular | `Post` |
| Job | Action description | `CreateUser` |
| Event | Tense indicates timing | `ApprovingLoan` / `LoanApproved` |
| Listener | Action + `Listener` | `SendInvitationMailListener` |
| Command | Action + `Command` | `PublishScheduledPostsCommand` |
| Mailable | Event/action + `Mail` | `AccountActivatedMail` |
| Resource | Plural + `Resource` | `UsersResource` |
| Enum | Descriptive, no prefix | `OrderStatus`, `Suit` |
---
## Spatie Package Architecture
### Package Structure (spatie/package-skeleton-laravel)
```
src/ # Main source code
src/Commands/ # Artisan commands
src/Components/ # Blade components
src/Contracts/ # Interfaces
src/Events/ # Events
src/Exceptions/ # Exception classes
src/Models/ # Eloquent models
src/Traits/ # Reusable traits
config/ # Config file (kebab-case, no `laravel-` prefix)
database/factories/ # Model factories
database/migrations/ # Migrations (stubs)
resources/views/ # Views
routes/ # Routes
tests/ # Tests (Pest)
```
### Service Provider Pattern
All Spatie Laravel packages extend `PackageServiceProvider` from `spatie/laravel-package-tools`:
```php
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
class MediaLibraryServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('laravel-medialibrary') // Package name
->hasConfigFile('media-library') // Config without 'laravel-' prefix
->hasMigration('create_media_table')
->hasViews('media-library')
->hasCommands([
RegenerateCommand::class,
ClearCommand::class,
CleanCommand::class,
]);
}
public function packageBooted(): void
{
// Post-boot logic: observers, event listeners, gate registrations
}
public function packageRegistered(): void
{
// Bindings, singletons, scoped instances
}
}
```
**Key lifecycle methods:**
- `configurePackage()` — declare assets (config, migrations, views, commands)
- `packageRegistered()` — bind interfaces, register singletons
- `packageBooted()` — register observers, macros, blade directives
### Namespace Conventions
- Root namespace: `Spatie\PackageName` (e.g., `Spatie\Permission`, `Spatie\MediaLibrary`)
- Packagist name: `spatie/laravel-package-name` or `spatie/package-name`
- Config file drops the `laravel-` prefix: `spatie/laravel-permission` → `config/permission.php`
### Contracts Pattern
Spatie packages use contracts (interfaces) for extensibility:
```php
// src/Contracts/Permission.php
namespace Spatie\Permission\Contracts;
interface Permission
{
// ...
}
// src/Models/Permission.php — implements the contract
class Permission extends Model implements PermissionContract
{
// ...
}
// Service provider binds contract to implementation
$this->app->bind(PermissionContract::class,
fn ($app) => $app->make($app->config['permission.models.permission'])
);
```
Config allows users to swap model implementations:
```php
// config/permission.php
return [
'models' => [
'permission' => Spatie\Permission\Models\Permission::class,
'role' => Spatie\Permission\Models\Role::class,
],
'table_names' => [
'roles' => 'roles',
'permissions' => 'permissions',
],
];
```
### Model Patterns
- Use `$guarded = []` (not `$fillable`).
- Configurable table names via config: `$this->table = config('permission.table_names.permissions') ?: parent::getTable();`
- Provide stRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.