amjadiqbal/filament-xlsx-import
A Filament plugin for importing XLSX spreadsheets directly into your resources, with a simple upload flow and column mapping so you can bring spreadsheet data into your admin panel without writing custom import logic.
amjadiqbal/filament-xlsx-import adds .xlsx (and optionally .csv) support to Filament's native importer, so you can keep using Filament\Actions\Imports\Importer classes and the standard import action instead of hand-rolling openspout parsing.
Install and update:
composer require amjadiqbal/filament-xlsx-import
# later
composer update amjadiqbal/filament-xlsx-import
Look first at the README's "large files" section and the plugin's own CSV example file — the v1.0.1 notes explicitly align that example with core, so it is the safest copy-paste starting point.
Wire the plugin's import component into a Filament action the same way you would core's CSV importer, then point it at your existing Importer class (getColumns(), resolveRecord(), castState() etc. are unchanged).
First use case: a resource page or table header action where an admin uploads an .xlsx export of your model, maps columns in the preview step, and imports. Start with a small sheet and the default sheet (index 0) before adding sheet() / headerOffset().
Reuse core importer classes. The package is a file-format layer, not a second import framework. Keep your Importer subclasses identical to what you'd write for CSV; only the upload/preview side changes. This keeps tests and ImportDispatcher behaviour familiar.
Sheet selection for multi-tab workbooks. When a workbook carries several tabs (e.g. one per region), call sheet('Orders') on the import component and generate one import per sheet, or expose the sheet name as a form field the user picks before upload.
Header offsets for report-style files. headerOffset(2) reads row 3, and empty rows count. Use it for exports that prepend a title/timestamp block. Assert the offset against a fixture, since an off-by-one silently maps the wrong labels to columns.
CSV and XLSX in one flow. Leave acceptCsv at its default if you want parity with core's CSV handling; set acceptCsv(false) when the XLSX path is the supported one and you want a clear "save as .xlsx" message rather than a silently different code path.
Chunking and queueing. The rows are streamed once when the import starts, but ImportDispatcher builds every chunk's job during that initial request. For big files, raise chunk size thoughtfully, run the import on a queue, and consider splitting very large workbooks into per-sheet or per-range imports so the request that builds jobs stays cheap.
Integration tips. Register the plugin in your panel provider alongside Filament's import action; keep column mapping in the importer, not in the upload form; and use the preview step as the validation gate for header offsets and sheet names.
sheet('X') failures changed message in v1.0.1. A missing sheet now throws The workbook has no sheet called "X". If you assert on exception text, update it; better, catch and surface it as a validation error before dispatch..xls is not supported. With acceptCsv(false) the browser accepts .xls so the "save as .xlsx" message can be shown. Don't read that as .xls parsing support.headerOffset() counts empty rows. headerOffset(2) is row 3 literally. Blank spacer rows shift your mapping — verify with a fixture that mirrors the real export.composer update. If you were working around the generic "could not be read as a spreadsheet" message or the .xls file-picker behaviour, remove those workarounds.Importer (casts, resolveRecord, beforeSave/afterSave hooks) rather than subclassing the plugin's upload component, so future patches stay drop-in.How can I help you explore Laravel packages today?