artisanpack-ui/icons
Register and use your own SVG icon sets in Laravel with minimal overhead. Integrates with blade-ui-kit/blade-icons and Livewire UI, supports config or event-based registration, and makes it easy to add premium sets like Font Awesome Pro.
This guide is designed for third-party package developers who want to integrate with ArtisanPack UI Icons and register their own icon sets programmatically. The Extension API allows packages to register icon sets via event hooks without requiring users to manually configure them.
The ArtisanPack UI Icons package provides a dual registration system:
config/artisanpack/icons.phpThe event-driven system is perfect for packages that want to bundle their own icon sets and register them automatically when installed.
ap.icons.register-icon-sets Filter HookThe primary extension point is the ap.icons.register-icon-sets filter hook. This hook allows packages to register icon sets that will be merged with user-configured sets.
use ArtisanPackUI\Icons\Registries\IconSetRegistration;
// In your package's service provider boot() method
addFilter('ap.icons.register-icon-sets', function (IconSetRegistration $registry) {
$registry->addSet(__DIR__ . '/../../resources/icons', 'mypackage');
return $registry;
});
The filter hook receives an IconSetRegistration registry instance and must return it after adding your icon sets via the addSet() or addSets() methods.
The IconSetRegistration class provides a structured way to define icon sets for event-driven registration.
public function __construct(
public string $path,
public string $prefix,
public ?string $disk = null
)
$path (string, required): Absolute path to the directory containing SVG files$prefix (string, required): Unique prefix for the icon set (e.g., 'mypackage', 'admin')$disk (string, optional): Laravel filesystem disk name (defaults to local filesystem)use ArtisanPackUI\Icons\Registries\IconSetRegistration;
$iconSet = new IconSetRegistration(
path: __DIR__ . '/../resources/icons',
prefix: 'admin'
);
$iconSet = new IconSetRegistration(
path: 'icons/custom',
prefix: 'cloud',
disk: 's3'
);
For packages that provide one icon set:
<?php
namespace MyPackage\Providers;
use Illuminate\Support\ServiceProvider;
use ArtisanPackUI\Icons\Registries\IconSetRegistration;
class MyPackageServiceProvider extends ServiceProvider
{
public function boot()
{
// Register your package's icon set
addFilter('ap.icons.register-icon-sets', function ($sets) {
$sets[] = new IconSetRegistration(
path: __DIR__ . '/../../resources/icons',
prefix: 'mypackage'
);
return $sets;
});
}
}
For packages that provide multiple themed icon sets:
public function boot()
{
addFilter('ap.icons.register-icon-sets', function (IconSetRegistration $registry) {
// Register multiple themed icon sets
$iconSets = [
'admin' => __DIR__ . '/../../resources/icons/admin',
'user' => __DIR__ . '/../../resources/icons/user',
'sys' => __DIR__ . '/../../resources/icons/system',
];
foreach ($iconSets as $prefix => $path) {
$registry->addSet($path, $prefix);
}
return $registry;
});
}
Register icon sets based on configuration or environment:
public function boot()
{
addFilter('ap.icons.register-icon-sets', function ($sets) {
// Only register if the package is configured to provide icons
if (config('mypackage.provide_icons', true)) {
$sets[] = new IconSetRegistration(
path: __DIR__ . '/../../resources/icons',
prefix: 'mypackage'
);
}
// Register additional sets based on features
if (config('mypackage.admin_icons', false)) {
$sets[] = new IconSetRegistration(
path: __DIR__ . '/../../resources/icons/admin',
prefix: 'myadmin'
);
}
return $sets;
});
}
Handle different installation contexts:
public function boot()
{
addFilter('ap.icons.register-icon-sets', function ($sets) {
// Resolve path based on package installation location
$iconPath = $this->resolveIconPath();
if ($iconPath && is_dir($iconPath)) {
$sets[] = new IconSetRegistration(
path: $iconPath,
prefix: 'dynamic'
);
}
return $sets;
});
}
private function resolveIconPath(): ?string
{
// Try multiple possible locations
$possiblePaths = [
__DIR__ . '/../../resources/icons',
resource_path('vendor/mypackage/icons'),
storage_path('app/mypackage/icons'),
];
foreach ($possiblePaths as $path) {
if (is_dir($path)) {
return $path;
}
}
return null;
}
public function boot()
{
addFilter('ap.icons.register-icon-sets', function ($sets) {
try {
$iconPath = __DIR__ . '/../../resources/icons';
// Validate path exists and is readable
if (!is_dir($iconPath) || !is_readable($iconPath)) {
\Log::warning("MyPackage icons directory not found or not readable: {$iconPath}");
return $sets;
}
// Check for SVG files
$svgFiles = glob($iconPath . '/*.svg');
if (empty($svgFiles)) {
\Log::info("MyPackage icons directory contains no SVG files: {$iconPath}");
return $sets;
}
$sets[] = new IconSetRegistration(
path: $iconPath,
prefix: 'mypackage'
);
\Log::info("MyPackage registered " . count($svgFiles) . " icons with prefix 'mypackage'");
} catch (\Exception $e) {
\Log::error("Failed to register MyPackage icons: " . $e->getMessage());
}
return $sets;
});
}
If registration order matters, you can use priority parameters:
// Register with high priority (early registration)
addFilter('ap.icons.register-icon-sets', function ($sets) {
// Critical system icons registered first
array_unshift($sets, new IconSetRegistration(
path: __DIR__ . '/../../resources/icons/critical',
prefix: 'critical'
));
return $sets;
}, 5); // Lower number = higher priority
// Register with normal priority
addFilter('ap.icons.register-icon-sets', function ($sets) {
$sets[] = new IconSetRegistration(
path: __DIR__ . '/../../resources/icons',
prefix: 'mypackage'
);
return $sets;
}, 10); // Default priority
The system handles conflicts between different registration sources with the following precedence:
// Good - unique and descriptive
$sets[] = new IconSetRegistration(
path: __DIR__ . '/../../resources/icons',
prefix: 'mycompany-admin'
);
// Avoid - too generic
$sets[] = new IconSetRegistration(
path: __DIR__ . '/../../resources/icons',
prefix: 'admin' // Could conflict with other packages
);
addFilter('ap.icons.register-icon-sets', function ($sets) {
// Check if our preferred prefix is already in use
$existingPrefixes = array_column($sets, 'prefix');
$prefix = 'mypackage';
if (in_array($prefix, $existingPrefixes)) {
$prefix = 'mypackage-alt'; // Use fallback prefix
\Log::warning("MyPackage icon prefix conflict detected, using fallback: {$prefix}");
}
$sets[] = new IconSetRegistration(
path: __DIR__ . '/../../resources/icons',
prefix: $prefix
);
return $sets;
});
Organize your package's icons for easy maintenance:
packages/mypackage/
├── resources/
│ └── icons/
│ ├── actions/
│ │ ├── save.svg
│ │ └── cancel.svg
│ ├── navigation/
│ │ ├── home.svg
│ │ └── back.svg
│ └── status/
│ ├── success.svg
│ └── error.svg
└── src/
└── MyPackageServiceProvider.php
Follow consistent naming for better developer experience:
user-profile.svg, arrow-left.svgtrash-can.svg instead of delete.svgnav-home.svg, nav-settings.svgDocument your icon set in your package's README:
## Icons
This package automatically registers icon components with the `mypackage` prefix:
- `<x-icon-mypackage-save />` - Save action icon
- `<x-icon-mypackage-cancel />` - Cancel action icon
- `<x-icon-mypackage-home />` - Home navigation icon
### Disabling Icons
To disable automatic icon registration:
```php
// config/mypackage.php
return [
'provide_icons' => false,
];
The complete usage examples with proper prefixes and styling classes are available in your package documentation.
Test your icon registration logic:
<?php
namespace Tests\Unit;
use Tests\TestCase;
use ArtisanPackUI\Icons\Registries\IconSetRegistration;
class IconRegistrationTest extends TestCase
{
public function test_package_registers_icon_set()
{
// Simulate the filter hook
$sets = [];
$sets = apply_filters('ap.icons.register-icon-sets', $sets);
// Assert your package's icon set is registered
$prefixes = array_column($sets, 'prefix');
$this->assertContains('mypackage', $prefixes);
// Find your package's registration
$myPackageSet = collect($sets)->first(fn($set) => $set->prefix === 'mypackage');
$this->assertNotNull($myPackageSet);
$this->assertInstanceOf(IconSetRegistration::class, $myPackageSet);
}
public function test_icon_files_exist()
{
$iconPath = __DIR__ . '/../../resources/icons';
$this->assertDirectoryExists($iconPath);
$svgFiles = glob($iconPath . '/*.svg');
$this->assertNotEmpty($svgFiles, 'No SVG files found in icon directory');
}
}
Test that icons render correctly:
public function test_package_icons_render_in_blade()
{
// Assuming you have a test icon file
$iconPath = __DIR__ . '/../../resources/icons/test.svg';
file_put_contents($iconPath, '<svg><circle r="10"/></svg>');
$view = $this->blade('<x-icon-mypackage-test />');
$this->assertStringContains('<svg', $view);
$this->assertStringContains('<circle r="10"/>', $view);
unlink($iconPath); // Cleanup
}
Add debugging to your registration:
addFilter('ap.icons.register-icon-sets', function ($sets) {
if (app()->environment('local')) {
\Log::debug('MyPackage registering icons', [
'path' => __DIR__ . '/../../resources/icons',
'prefix' => 'mypackage',
'existing_sets_count' => count($sets)
]);
}
$sets[] = new IconSetRegistration(
path: __DIR__ . '/../../resources/icons',
prefix: 'mypackage'
);
return $sets;
});
How can I help you explore Laravel packages today?