Import Wizard
CommunityQueue-powered CSV and Excel import wizard for Filament with smart column mapping, relationship linking, and background processing rows.
Author:
Waad Mawlood
Package health
BetaAutomated checks of this plugin's Composer package
13 checks
-
Passed:
Current Laravel version supported
—
Package dependencies resolve together with current Laravel
13.0. -
Passed:
Current PHP version supported
—
Constraint
^8.2supports current PHP8.5. - Skipped: Current Symfony version supported
- Passed: Abandoned or archived — No consulted source marks the package abandoned (packagist, github).
- Passed: Commit and release recency — Active: last commit 53 days ago; last release 53 days ago.
-
Passed:
composer.lock not committed by library
—
composer.lockis absent from the released dist archive. - Passed: Dist archive is lean
- Skipped: GitHub Actions pinned to SHA
- Passed: Open security advisories
- Passed: Dependabot PR responsiveness — No open Dependabot PRs.
- Skipped: Dependabot or Renovate configured
- Skipped: Dependency update cooldown configured
- Failed: Provides a security policy — View details on Plumb
filament/
namespace. Review the source and install at your own risk. Found
malware or an unresolved security issue the author won't
address?
Report it
.
Documentation
- ✨ Features
- 📦 Installation
- 🚀 Usage
- ⚙️ Configuration
- 📋 Import Steps
- 🔗 Relationship Linking
- 🔃 Upsert (Match & Merge)
- 🌍 Spatie Translatable & Locale Merge
- 🧠 Smart Defaults & Guarded Model Support
- 🛠️ Customization
- 📝 Requirements
- 📄 License
- 🤝 Contributing
- 📧 Support
- Screenshots
A powerful, queue-powered CSV and Excel import wizard for Filament with smart column mapping, relationship linking, Spatie Translatable support, locale merge, dynamic schema validation, and background processing for 100K+ rows.
#✨ Features
- 4-Step Import Wizard — Upload, map columns, review data, and import inside a Filament modal
- Reverse Mapping UI — Model fields shown first with CSV column dropdowns, not the other way around
- Smart Auto-Mapping — Automatically maps CSV headers to model fields using snake_case matching (e.g.,
Title→title,CategoryId→category_id) - Relationship Linking — Link or create related records (
BelongsTo) with intelligent auto-increment ID handling - Spatie Translatable Support — Detect
HasTranslationstraits and JSON columns; supportswhereJsonContains()for cross-DB translated lookups - Locale Merge — Combine multiple language columns (e.g.,
title_en,title_ar) into a single JSON translatable field with checkbox toggle - Locale-Aware Relation Lookup — Find or create related models using translated column values
- Upsert Support — Update existing records instead of creating duplicates via configurable keys
- Queue-Powered Processing — Chunked batch execution with live progress tracking for large datasets
- Eloquent Event Firing — Inserts fire model
boot(), observers, timestamps, and lifecycle hooks via individualsave() - Dynamic Schema Validation — Auto-generate validation rules from database column types (required, numeric, boolean, date, email, unique)
- Error Review — Inline validation and error download before import
- Multi-Tenancy Ready — Built-in team and tenant scoping across queue boundaries
- Standalone Mode — Use as a Livewire component outside Filament panels
- Dark Mode — Full dark theme support out of the box
- Custom Queue Configuration — Configurable queue connection and queue name per import
- Excel Empty Column Trimming — Automatically strips trailing empty Excel columns (no more 256-column UI)
#📦 Installation
composer require waad/filament-import-wizard
#Publish Configuration & Migrations
# Publish config file (optional)
php artisan vendor:publish --tag="filament-import-wizard-config"
# Publish and run migrations
php artisan vendor:publish --tag="filament-import-wizard-migrations"
php artisan migrate
⚠️ If there are errors for CSS, try rebuilding Filament assets:
php artisan filament:assets
#🚀 Usage
#Basic Usage in Filament Resource
Add the import action to your Filament resource's table.
Example (app/Filament/Resources/Posts/Pages/ListPosts.php):
use Waad\FilamentImportWizard\Actions\ImportWizardAction;
protected function getHeaderActions(): array
{
return [
ImportWizardAction::make(),
];
}
#Advanced Configuration
ImportWizardAction::make()
->forModel(\App\Models\Product::class)
->chunkSize(500) // Process 500 rows per job
->enableUpsert(true) // Update existing records
->upsertKeys(['sku']) // Match by SKU field
->queueConnection('redis') // Custom queue connection
->queueName('imports'); // Custom queue name
#Standalone Usage
Use the wizard outside of Filament panels via Livewire:
@livewire('filament-import-wizard', ['modelClass' => \App\Models\Product::class])
#⚙️ Configuration
// config/filament-import-wizard.php
return [
'modal_width' => Width::Full, // Modal width (Filament Width enum)
'chunk_size' => 1000, // Rows per queue job
'default_csv_delimiter' => ',', // CSV delimiter (comma, semicolon, tab)
'queue_connection' => null, // Queue connection (null = default)
'queue_name' => null, // Queue name (null = default)
];
#Configuration Options
| Option | Default | Description |
|---|---|---|
modal_width |
Width::Full |
Width of the import wizard modal |
chunk_size |
1000 |
Number of rows processed per queue job |
default_csv_delimiter |
, |
Default CSV delimiter for parsing |
queue_connection |
null |
Queue connection to use (null = Laravel default) |
queue_name |
null |
Specific queue name (null = default queue) |
#📋 Import Steps
#Step 1: Upload
Upload your CSV or Excel file. Supported formats:
- CSV (
.csv) — with UTF-8 BOM auto-detection - Excel (
.xlsx,.xls) — trailing empty columns are automatically trimmed
#Step 2: Map Columns
The mapping step presents model fields first, with a clean table-based layout:
- Model Fields Section — Each model field is displayed with its label, code name, and a dropdown to select which CSV column maps to it
- Relations Section — Each
BelongsTorelation is shown with its FK/PK badges and a searchable field dropdown - Unmapped Columns Section — CSV columns that are not mapped appear at the bottom
#Locale Merge (Translatable Fields)
For translatable/JSON columns, toggle the Merge Translation switch to split a field into multiple locale→column mappings:
Title (translatable) → Merge Translation ON
[en] → title_en
[ar] → title_ar
[fr] → title_fr
The locale can be auto-detected from header names (title_en, titleAr, etc.) or entered manually. This stores data as JSON: {"en": "Hello", "ar": "مرحبا"}.
#Relation Mapping
For BelongsTo relations:
Category → [Select CSV column...]
FK: category_id PK: id
[Search fields...] [name ▼]
Relations support auto-increment ID detection (if CSV has numeric IDs) and intelligent fallback to string field matching.
#Step 3: Review & Validate
Preview your data before import:
- View first 100 rows with mapped columns
- See validation errors per row with error badges
- Dynamic rules auto-generated from database schema (column types, nullable, unique)
- Relation foreign keys bypass type constraints in validation (labels are resolved later)
- Configure upsert settings (enable/disable, keys)
#Step 4: Import
Start the import process:
- Background queue processing with live progress polling every 5 seconds
- Sync queue runs jobs immediately for testing
- Eloquent mode (non-upsert): Records are saved individually via
save(), firingboot(), events, and observers - Upsert mode: Bulk
upsert()with fallback to chunked inserts on failure - Error tracking with downloadable CSV error logs
- Final summary with animated success/error counts and
completed_with_errorsstatus
#🔗 Relationship Linking
Link related records during import:
// Example: Import products and link to categories
CSV Column: "Category Name" → Relation: category → Field: name
Supported relationship types:
BelongsTo— with automatic FK/PK resolution
#How Relation Resolution Works
- If the owner key is an auto-incrementing primary key:
- Numeric CSV values → find or create by ID
- String CSV values → find or create by
namefield, or auto-detect a suitable string column
- If the owner key is a non-ID column → direct lookup by that column
- Translatable/locale-aware resolution via
whereJsonContains()for JSON fields
#🔃 Upsert (Match & Merge)
Update existing records instead of creating duplicates:
ImportWizardAction::make()
->forModel(\App\Models\User::class)
->enableUpsert(true)
->upsertKeys(['email']); // Match users by email
The wizard will:
- Find existing records by the specified keys
- Update matching records via
upsert()(with timestamp injection) - Create new records only when no match is found
- Fall back to chunked inserts on upsert failure
#🌍 Spatie Translatable & Locale Merge
#Automatic Detection
The wizard automatically detects:
- Models using
Spatie\Translatable\HasTranslationstrait - JSON/JSONB columns from the database schema
#Locale Merge Mode
Check Merge Translation on any translatable field to split it into locale-specific mappings:
| CSV Header | Maps To |
|---|---|
title_en |
title with locale en |
title_ar |
title with locale ar |
title_fr |
title with locale fr |
The result is stored as a single JSON column: {"en": "value", "ar": "قيمة", "fr": "valeur"}
#Custom Import Field Labels
Add a getImportFieldLabel() method to your model for custom display names:
class Product extends Model
{
public function getImportFieldLabel(string $field): string
{
return match($field) {
'sku' => 'SKU (Stock Keeping Unit)',
'price_in_cents' => 'Price (cents)',
default => Str::title(str_replace(['_', '.'], ' ', $field)),
};
}
}
#🧠 Smart Defaults & Guarded Model Support
The wizard handles models with $guarded = [] (no fillable defined) gracefully:
- Dynamic fillable resolution: Falls back to
Schema::getColumnListing()whengetFillable()is empty - All columns allowed: When fillable is empty, any CSV column can map to any database column
- Foreign key resolution: Bypasses fillable checks when model is fully unguarded
#🛠️ Customization
#Custom Modal Width
use Filament\Support\Enums\Width;
ImportWizardAction::make()
->forModel(\App\Models\Product::class)
->setModalWidth(Width::ExtraLarge);
#Queue Configuration
Set queue connection and name globally via config:
// config/filament-import-wizard.php
return [
'queue_connection' => 'redis',
'queue_name' => 'imports',
];
Or per import action:
ImportWizardAction::make()
->forModel(\App\Models\Product::class)
->queueConnection('redis')
->queueName('imports');
#Custom Validation Rules
Add a getImportRules() method to your model for custom import validation:
class Product extends Model
{
public function getImportRules(): array
{
return [
'sku' => ['required', 'string', 'max:50'],
'price' => ['required', 'numeric', 'min:0'],
'stock' => ['nullable', 'integer', 'min:0'],
];
}
}
If no getImportRules() method exists, rules are auto-generated from the database schema.
#📝 Requirements
- PHP: 8.2+
- Laravel: 10+
- Filament: 4.x or 5.x
#📄 License
The MIT License (MIT). Please see License File for more information.
#🤝 Contributing
Contributions are welcome! Please open an issue or submit a pull request.
#📧 Support
If you discover any bugs or have feature requests, please open an issue on GitHub.
#Screenshots

The author
From the same author
Featured Plugins
A selection of plugins curated by the Filament team
Blueprint
Filament Blueprint is a premium Laravel Boost extension that helps AI agents produce accurate, detailed implementation plans and security reports for Filament apps.
Filament
Spotlight Pro
Browse your Filament Panel with ease. Filament Spotlight Pro adds a Spotlight/Raycast like Command Palette to your Filament Panel.
Dennis Koch
Custom Dashboards
Let your users build and share their own dashboards with a drag-and-drop interface. Define your data sources in PHP and let them do the rest.
Filament