jeffersongoncalves/filament-action-export
Export Filament tables to CSV, XLSX, and PDF with preview, print support, and full customization. Adds a bulk export action to your tables, with optional config, view, and translation publishing. Supports Filament 3.x, 4.x, and 5.x.
Architecture fit. The package is Filament-native rather than framework-adjacent: it ships two action classes (FilamentExportBulkAction, FilamentExportHeaderAction) that drop into ->bulkActions([]) / ->headerActions([]) and a shared InteractsWithExportAction trait (extracted in 3.7.0) that single-sources config defaults. Since 3.1.0 the panel plugin was removed, so there is no service-provider registration decision to make — integration is pure resource configuration, which matches how our panels are already assembled. The fluent API (->formats(), ->excludeColumns(), ->modifyQueryUsing()) reads like first-party Filament, so it is legible to any dev who knows the framework. Underlying dependencies are mainstream (Spatie SimpleExcelWriter for CSV/XLSX, barryvdh/laravel-dompdf with optional Snappy for PDF), all MIT/BSD and already common in Laravel estates. No migrations, no new tables, no external services.
Integration feasibility. One composer require, optional publish of config/views/translations, and a handful of lines per table. First working export is a 1–3 dev-day effort; a vetted internal wrapper plus authorization/audit work is 3–5 days. Exit cost is low: the public surface is small and the heavy lifting sits in libraries we could call directly.
Technical risk. Ranked by what would actually page someone:
| Risk | Severity | Why |
|---|---|---|
| Adoption vacuum | High | 8 stars, 0 dependents, single maintainer. Nobody else is running this in production that we can point to. Bus factor = 1. |
| Silent no-op behaviour | High | Changelog v3.2.0 states withFilters()/withSearch()/withSort() were "previously declared but non-functional." For exports, a silently unfiltered dataset is a data-disclosure incident, not a bug. |
| API churn | Medium | v3.0.0 → v3.7.0 in a compressed window, including a removed plugin and changed print mechanics (v3.6.9 fixed empty print output). Fast iteration is good for a solo author, noisy for a pinned consumer. |
| Version cliff | Medium | Branch-per-Filament-major (1.x/3.x, 2.x/4.x, 3.x/5.x). Fixes like the 3.6.9 print/state-path repair land on 3.x only; if we are on Filament 3 or 4 we inherit a stale branch. |
| Synchronous generation | Medium | chunk_size: 1000 governs DB reads, not request lifetime. Nothing in the README indicates queued or streamed exports. |
| Data egress surface | High | A header action applies filters/search/sort and exports everything matching. Row-level policy scoping is our responsibility, not the package's. |
| PDF driver path | Medium | DomPDF memory profile; Snappy shells out to wkhtmltopdf (binary availability, sandboxing, process limits). modifyPdfWriter(fn ($pdf) => $pdf->setWarnings(false)) is documented as the fix-all — silent failure by design. |
| Supply chain | Low–Medium | Transitive dompdf has a non-trivial CVE history; must clear our dependency policy. |
Key questions to resolve before adoption.
extraViewData() escaped in the PDF/print Blade templates, or do we own that risk?Stack fit. Declared requirements: PHP ^8.2, Laravel ^11, Filament ^5, Livewire ^4. We must confirm against composer.lock, not the README — a Filament 4 estate means v2.x and a different risk conversation. Nine bundled locales (en, pt_BR, es, fr, de, it, nl, ar, tr) cover most of our admin surface; anything outside that list is a publish-and-translate task.
Migration path. Two scenarios: (a) greenfield — add actions to the resources that need export and move on; (b) replacing an existing exporter such as pxlrbt/filament-excel — do not run both on the same table, pick one per resource and migrate table-by-table so users see one export button, not two. There is no data migration; the only state is configuration. The exit ramp is cheap because we will wrap it (below), so a future swap is a single class rewrite rather than a codebase sweep.
Compatibility and contract discipline.
composer.json ("^3.7" at most), never a floating constraint.default_format and pdf_driver env keys in staging — config drift here is a classic "works on my machine" export break.Sequencing.
modifyQueryUsing. Confirm withFilters()/withSearch()/withSort() actually produce filtered output — assert on row counts.App\Support\Exports\TableExportAction exposing only the options we have vetted, with our defaults (locale, delimiter, orientation, filename convention, excludeColumns for known-sensitive fields). This is the single highest-leverage step for a TPM: it converts a third-party dependency into an internal contract.export ability, mirror tenant global scopes to be safe, log (user, resource, filters, row_count, format, timestamp).Maintenance. Budget roughly half a day per month: dependency bumps (Renovate/Dependabot with manual review, not auto-merge), one changelog read per release, and a re-run of the export smoke tests. Because the project iterates quickly as a solo effort, we should maintain a private fork or vendor patch capability from day one — cheap now, expensive during an incident. If any views are published, add a release checklist item to diff upstream templates.
Support. There is no commercial support and effectively no community support at 8 stars / 0 dependents. Our SLA is our own engineering capacity. That means: a named internal owner, a runbook covering "export produces empty file," "PDF 500s," "filters ignored," and "download times out," plus a documented decision to fork if issue response goes cold for >30 days. Legal review is trivial (MIT), but the maintainer concentration should be logged as an accepted dependency risk.
Scaling. Establish hard thresholds rather than discovering them in production:
memory_limit and max_execution_time.Failure modes to instrument and alert on. Request timeout / OOM on large exports; empty print or empty PDF output (an observed defect class upstream — v3.6.9 fixed state-path resolution); silently unfiltered datasets (the highest-severity failure); missing wkhtmltopdf binary when Snappy is enabled; DomPDF remote-asset fetch stalls; filename collisions under concurrent exports; unescaped extraViewData injection into PDF templates; PII leaving the system with no audit record. Alert on export error rate, p95 export duration by row count, and export volume per user — an anomalous spike in the last metric is a data-exfiltration signal, not a UX metric.
Ramp-up. A senior Laravel dev is productive in half a day; two of the options (writer callbacks, modifyQueryUsing stacking) warrant a short internal note. Knowledge will concentrate in one or two people, so the wrapper plus runbook is the mitigation. Realistic team ramp: one week to confident usage across the squad.
TPM recommendation. Conditional adopt, scoped and wrapped: ship it to internal admin tables within one sprint behind a policy, keep it out of customer-facing and multi-tenant surfaces until the security review and audit logging are in place, pin the version, and hold a fork-ready posture. The integration cost is low enough that the main risk is not engineering effort — it is trusting an unvetted single-maintainer package with our data egress path.
How can I help you explore Laravel packages today?