Adding a CSV import wizard to your Filament panel | Tapix                  [ ![Tapix](/img/tapix-logo-light.svg) ![Tapix](/img/tapix-logo-dark.svg) ](https://tapix.dev) [Features](https://tapix.dev#features) [Pricing](https://tapix.dev#pricing) [Docs](https://docs.tapix.dev) [Blog](https://tapix.dev/blog)

   Try Demo  [ Get Tapix from $99](https://tapix.dev#pricing)

  [Features](https://tapix.dev#features) [Pricing](https://tapix.dev#pricing) [Docs](https://docs.tapix.dev) [Blog](https://tapix.dev/blog)   Try Demo  [ Get Tapix from $99](https://tapix.dev#pricing)

   ![Tapix](https://tapix.dev/img/tapix-logo-light.svg)

 TutorialsAdding a CSV import wizard to your Filament panel
=================================================

 tapix.dev/blog

  [    Back to blog ](https://tapix.dev/blog) [ Tutorials ](https://tapix.dev/blog/category/tutorials)

Adding a CSV import wizard to your Filament panel
=================================================

 Manch Minasyan ·  June 26, 2026  · 9 min read

 Filament panels already give you resource management, form builders, table views, and a full admin shell. What they do not give you is a multi-step import wizard with inline validation, relationship resolution, and queue-powered execution. Filament's built-in Import Action handles simple cases well, but when your users need to preview data, fix errors before committing, and link CSV columns to related models, you need something purpose-built.

Tapix was designed as a Filament plugin from day one. The integration adds an import wizard and an import history page to your existing panel -- same authentication, same sidebar, same tenant context. This tutorial walks through the full setup: installing the package, registering the plugin, enabling auto-discovery, and customizing the navigation to fit your panel's structure.

[\#](#step-1-install-the-package "Permalink")Step 1: Install the package
------------------------------------------------------------------------

Tapix ships as a Composer package. Require it in your project and publish the configuration file:

```
composer require tapix/core
php artisan vendor:publish --tag=tapix-config

```

This creates `config/tapix.php` with sensible defaults for queue configuration, chunk sizes, table prefixes, and tenant settings. You can adjust these later, but the defaults work out of the box for most applications.

If you do not already have importers in your project, generate one to have something to work with:

```
php artisan make:tapix-importer Contact

```

This creates `app/Importers/ContactImporter.php` with a skeleton class extending `BaseImporter`. The skeleton includes the two methods every importer needs: `model()` to declare the target Eloquent model, and `fields()` to define importable columns with the `ImportField` builder. For customizing behavior at each stage of the import -- ownership assignment, relationship resolution, cache warming -- see [CSV import lifecycle hooks: running custom logic before, during, and after import](/blog/csv-import-lifecycle-hooks).

[\#](#step-2-register-the-plugin "Permalink")Step 2: Register the plugin
------------------------------------------------------------------------

Open your panel provider -- typically `app/Providers/Filament/AdminPanelProvider.php` -- and add the TapixPlugin to your panel's plugin chain:

```
use Tapix\Core\Filament\TapixPlugin;
use App\Importers\ContactImporter;
use App\Importers\ProductImporter;

public function panel(Panel $panel): Panel
{
    return $panel
        ->default()
        ->id('admin')
        ->path('admin')
        // ... your existing configuration
        ->plugin(
            TapixPlugin::make()
                ->importers([
                    ContactImporter::class,
                    ProductImporter::class,
                ])
        );
}

```

Three lines of actual configuration: `TapixPlugin::make()`, the `->importers()` call, and your importer class list. That registers two pages in your Filament panel: an import page where users pick an importer and walk through the wizard, and an import history page listing past imports with their status and row counts.

The plugin follows Filament's standard plugin pattern. It implements `FilamentPlugin`, registers its pages and middleware during `register()`, and wires up navigation during `boot()`. If you have written or installed other Filament plugins, this works exactly the same way.

[\#](#step-3-auto-discovery "Permalink")Step 3: Auto-discovery
--------------------------------------------------------------

Listing every importer class manually works fine when you have two or three. When you have fifteen, it becomes maintenance overhead that you should not have to think about. The `discoverImporters()` method scans a directory and registers every class it finds that extends `BaseImporter`:

```
->plugin(
    TapixPlugin::make()
        ->discoverImporters(
            in: app_path('Importers'),
            for: 'App\\Importers',
        )
)

```

This mirrors Filament's own `discoverResources()` and `discoverPages()` methods. Drop a new importer class into `app/Importers/` and it appears in the import page automatically. No registration, no config changes, no deployment ceremony.

You can combine explicit registration with auto-discovery if needed. Importers registered with `->importers([...])` take precedence over discovered ones, so you can override the label or sort order for specific importers while letting the rest auto-register.

For most applications, switch to `discoverImporters()` early and never think about importer registration again.

[\#](#the-import-page "Permalink")The import page
-------------------------------------------------

Once the plugin is registered, your panel's sidebar shows a new navigation item: Imports. Clicking it takes users to the import page, which lists all available importers as selectable cards.

Each card shows the importer's label and a short description. Users pick the importer that matches their data and click to start the wizard. The wizard flows through four steps:

1. **Upload** -- the user uploads a CSV file. Tapix parses headers and stores every row.
2. **Map** -- the wizard auto-detects column matches using the `guess()` aliases defined in your importer's fields. Users review and correct any mismatches.
3. **Review** -- validation runs per column. Errors appear inline, per cell. Users fix values directly in the table without leaving the application.
4. **Execute** -- rows are processed in chunked queue jobs with live progress feedback. The user can navigate away and come back -- all state is persisted to the database.

For how the import pipeline works under the hood -- the SQLite-per-import store, the parallel validation jobs, and the job architecture -- see [Tapix under the hood: how we built a 4-step import wizard](/blog/tapix-under-the-hood-architecture).

The import page is a thin Filament page wrapper around the `ImportWizard` Livewire component. It inherits your panel's layout, authentication, and theme. The wizard runs inside your existing admin shell, not in a separate window or route.

[\#](#the-history-page "Permalink")The history page
---------------------------------------------------

Every import creates an `Import` model record that tracks its status, row counts, and timestamps. The history page lists these records in a Filament table with columns for:

- **Importer** -- which importer class ran the import
- **Status** -- uploading, mapping, reviewing, importing, completed, or failed
- **Total rows** -- how many rows the file contained
- **Created / Updated / Skipped / Failed** -- breakdown of what happened to each row
- **Started at / Completed at** -- timing for the import run

Users can click into a completed import to see its details, including any failed rows with their error messages. Failed rows are downloadable as a CSV for cases where users need to re-import a subset after fixing data upstream.

The history page gives operations teams visibility into what has been imported, when, and by whom. No more guessing whether someone already ran the product catalog import this morning.

[\#](#tenant-context "Permalink")Tenant context
-----------------------------------------------

Multi-tenant applications face a specific challenge with CSV imports: the tenant context from the HTTP request disappears when processing moves to queue workers. A queue job has no authenticated user, no session, and no Filament tenant. If your entity lookups are not explicitly scoped, a contact import for Tenant A could match companies belonging to Tenant B.

Tapix handles this automatically in Filament panels. The plugin registers `SetTenantContextMiddleware`, which reads the current tenant from Filament's tenant system and stores it in the `TenantContextService`. When import jobs are dispatched to the queue, the `TenantAware` trait on each job serializes the tenant ID into the job payload and restores it on the worker side before any database queries run.

The result: every query in the import pipeline -- validation checks, relationship lookups, match resolution, and row creation -- is scoped to the correct tenant. You do not need to configure any of this. If your Filament panel has tenancy enabled, Tapix picks it up through the middleware.

For applications using custom tenancy implementations outside of Filament's built-in system, the `TenantContextService` accepts a resolver closure:

```
use Tapix\Core\Services\TenantContextService;

app(TenantContextService::class)->resolveUsing(function () {
    return auth()->user()?->current_team_id;
});

```

This is the escape hatch for non-standard setups. Most Filament applications will never need it.

For a deeper treatment of multi-tenant import architecture -- tenant serialization in jobs, scoped entity resolution, and cross-tenant leak prevention -- see [Multi-tenant CSV imports in Laravel](/blog/multi-tenant-csv-imports-laravel).

[\#](#customization "Permalink")Customization
---------------------------------------------

The plugin exposes a fluent API for adjusting how the import pages appear in your panel's navigation.

### [\#](#navigation-group "Permalink")Navigation group

By default, the import pages appear as top-level navigation items. To group them under an existing navigation group:

```
->plugin(
    TapixPlugin::make()
        ->discoverImporters(
            in: app_path('Importers'),
            for: 'App\\Importers',
        )
        ->navigationGroup('Data Management')
)

```

This is useful when your panel already has a structure like "Content", "Users", "Settings" and you want imports to live under a relevant heading rather than floating in the sidebar.

### [\#](#navigation-label-and-icon "Permalink")Navigation label and icon

Override the default label and icon for the import page:

```
TapixPlugin::make()
    ->discoverImporters(
        in: app_path('Importers'),
        for: 'App\\Importers',
    )
    ->navigationLabel('CSV Imports')
    ->navigationIcon(Heroicon::ArrowUpTray)

```

The icon accepts any value from Filament's `Heroicon` enum. The label is what users see in the sidebar. Both affect only the navigation rendering, not the page title or breadcrumbs.

### [\#](#sorting-and-visibility "Permalink")Sorting and visibility

Control where the import navigation item appears relative to other items:

```
TapixPlugin::make()
    ->discoverImporters(
        in: app_path('Importers'),
        for: 'App\\Importers',
    )
    ->navigationSort(5)

```

Lower numbers appear higher in the sidebar. This follows Filament's standard navigation sorting behavior.

[\#](#what-you-get-without-building "Permalink")What you get without building
-----------------------------------------------------------------------------

To be concrete about what the plugin setup replaces, here is what you would need to build yourself to achieve the same functionality without Tapix:

- A Filament page with a file upload form and CSV parser
- A column mapping UI that auto-detects headers and lets users correct mismatches
- Per-cell validation with inline error display and correction
- Queue jobs for row processing with chunked batches
- Progress tracking with live updates via database polling or broadcasting
- An import history table with status, counts, and failed row downloads
- Tenant context serialization into queue jobs
- Match resolution for relationship columns with configurable create/update/skip behavior

Each of these is a real component that needs to be built, tested, and maintained. The plugin registration replaces all of it with a few lines in your panel provider and an importer class that defines your fields.

[\#](#next-steps "Permalink")Next steps
---------------------------------------

If you are evaluating whether your import needs justify a dedicated wizard versus Filament's built-in Import Action, read [Filament Import Action: when it's enough and when you need more](/blog/filament-import-action-when-enough). That post gives an honest breakdown of where the built-in action works well and where it reaches its limits.

For a broader overview of all CSV import approaches in Laravel, start with [The complete guide to CSV imports in Laravel](/blog/complete-guide-csv-imports-laravel). It covers raw PHP, Laravel Excel, Filament Import Action, and Tapix side by side.

For the multi-tenancy story in detail, [Multi-tenant CSV imports in Laravel](/blog/multi-tenant-csv-imports-laravel) covers the full problem: tenant context in queue jobs, scoped entity resolution, and preventing cross-tenant data leaks.

Tapix is available at [tapix.dev](/). Same Filament panel, same authentication, same tenant context -- with an import wizard that handles everything the built-in action was not designed to do. [See current pricing](/#pricing).

 ### Enjoyed this post?

Get notified when we publish new articles about Laravel imports and data handling.

  Email address   Subscribe

Almost there — confirm your subscription via email.

 Related posts
-------------

 [  Tutorials   Jun 19, 2026

 Handling boolean and choice fields in CSV imports
---------------------------------------------------

Yes, no, true, false, 1, 0, on, off -- and that's just booleans. Here's how to normalize boolean, single-choice, and multi-choice CSV values.

 ](https://tapix.dev/blog/boolean-choice-fields-csv) [  Tutorials   Jun 12, 2026

 Date format detection in CSV imports: ISO, European, and American
-------------------------------------------------------------------

04/05/2026 -- is that April 5th or May 4th? How to detect and parse ambiguous date formats in CSV imports.

 ](https://tapix.dev/blog/date-format-detection-csv) [  Tutorials   May 29, 2026

 Building a contact importer for your CRM
------------------------------------------

End-to-end tutorial: build a CSV contact importer with name, email, phone, company relationships, choice fields, and currency parsing.

 ](https://tapix.dev/blog/building-contact-importer-crm)

   [ ![Tapix](/img/tapix-logo-light.svg) ![Tapix](/img/tapix-logo-dark.svg) ](https://tapix.dev)CSV and Excel import wizard for Laravel.

  Product [Pricing](https://tapix.dev#pricing) [Docs](https://docs.tapix.dev) [Blog](https://tapix.dev/blog) [Contact](mailto:hello@tapix.dev)

 Compare [vs Laravel Excel](https://tapix.dev/vs/laravel-excel) [vs Filament Import](https://tapix.dev/vs/filament-import)

 Legal [Privacy](https://tapix.dev/privacy-policy) [Terms](https://tapix.dev/terms-of-service)

© 2026 Tapix. All rights reserved.
