Cómo exportar archivos Excel en Laravel 13: guía completa con Laravel Excel
Generar un Excel descargable es uno de los requisitos más habituales en cualquier panel de administración: exportar el listado de pedidos, un reporte de ventas o la base de clientes para que el equipo lo abra en su hoja de cálculo. En Laravel, el estándar sigue siendo el paquete maatwebsite/excel (versión 3.1.69 a abril de 2026), el mismo que usamos para importar archivos Excel.
En esta guía actualizada para Laravel 13 vas a ver cómo crear una clase de exportación, añadir encabezados y estilos, controlar el formato de las celdas, exportar a distintos formatos (XLSX, CSV) y, lo más importante para producción, mover exportaciones pesadas a cola para no reventar la memoria del servidor.
1. Tener Laravel 13 instalado
Si todavía no tienes el proyecto listo, revisa la guía dedicada: Cómo instalar Laravel 13. Recuerda que Laravel 13 requiere PHP 8.3 como mínimo (8.4 en la práctica desde Laravel 13.3).
2. Instalación del paquete maatwebsite/excel
Instala el paquete vía Composer:
composer require maatwebsite/excel
El paquete registra automáticamente su service provider y el facade Excel. Si quieres ajustar la configuración por defecto (formato de fechas, disco de almacenamiento, etc.), publica el archivo de config:
php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config
Esto crea config/excel.php, donde puedes tocar cosas como el csv delimiter o el disco por defecto.
3. Creación de la clase de exportación
Toda la lógica de exportación vive en una clase dedicada dentro de app/Exports. Genérala con Artisan asociándola a un modelo:
php artisan make:export PedidosExport --model=Pedido
Esto crea app/Exports/PedidosExport.php con una clase que implementa el concern FromCollection:
<?php
namespace App\Exports;
use App\Models\Pedido;
use Maatwebsite\Excel\Concerns\FromCollection;
class PedidosExport implements FromCollection
{
public function collection()
{
return Pedido::all();
}
}
El método collection() devuelve los datos que se volcarán en el archivo. Ya con esto tienes un Excel funcional, pero le faltan encabezados y formato — lo vamos a mejorar con los concerns de las siguientes secciones.
4. Las fuentes de datos: FromCollection, FromQuery, FromView
maatwebsite/excel te deja elegir de dónde salen los datos según el caso. Estas son las cuatro opciones principales:
FromCollection— devuelves una colección de Eloquent o un array. Sencillo, pero carga todo en memoria. Ideal para volúmenes pequeños.FromQuery— devuelves una query sin ejecutar y el paquete la procesa en chunks automáticamente. La opción recomendada para tablas grandes.FromView— renderizas una vista Blade con una<table>HTML. Perfecto cuando necesitas control total del layout o estilos con HTML.FromArray— para datos que ya tienes en un array plano.
Ejemplo con FromQuery, mucho más eficiente en memoria:
<?php
namespace App\Exports;
use App\Models\Pedido;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;
class PedidosExport implements FromQuery
{
use Exportable;
public function query()
{
return Pedido::query()->where('estado', 'completado');
}
}
5. Encabezados y mapeo de columnas
Un Excel sin encabezados no sirve de mucho. Añade el concern WithHeadings para la fila de títulos y WithMapping para decidir exactamente qué columnas y en qué formato se exporta cada fila:
<?php
namespace App\Exports;
use App\Models\Pedido;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class PedidosExport implements FromQuery, WithHeadings, WithMapping
{
public function query()
{
return Pedido::query()->with('cliente');
}
public function headings(): array
{
return ['ID', 'Cliente', 'Total', 'Estado', 'Fecha'];
}
public function map($pedido): array
{
return [
$pedido->id,
$pedido->cliente->nombre,
number_format($pedido->total, 2),
ucfirst($pedido->estado),
$pedido->created_at->format('d/m/Y'),
];
}
}
Fíjate en el with('cliente'): cargar la relación por adelantado evita el problema N+1 cuando exportas miles de filas. El método map() es también el sitio ideal para formatear fechas y montos como los quiere ver el usuario final.
6. Estilos y formato de celdas
Para que el reporte se vea profesional, aplica ancho automático, negrita en los encabezados y formato numérico correcto:
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
class PedidosExport implements FromQuery, WithHeadings, WithMapping, ShouldAutoSize, WithStyles, WithColumnFormatting
{
// ...query(), headings() y map() como antes...
public function styles(Worksheet $sheet)
{
return [
1 => ['font' => ['bold' => true]], // encabezados en negrita
];
}
public function columnFormats(): array
{
return [
'C' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // columna Total
];
}
}
ShouldAutoSizeajusta el ancho de las columnas al contenido.WithStyleste da acceso al objetoWorksheetde PhpSpreadsheet para estilos avanzados (colores, bordes, alineación).WithColumnFormattingaplica formatos de número, moneda o fecha por columna.
7. Descargar y almacenar el archivo
Con la clase lista, dispararla desde un controlador es trivial. El facade Excel ofrece dos operaciones principales:
<?php
namespace App\Http\Controllers;
use App\Exports\PedidosExport;
use Maatwebsite\Excel\Facades\Excel;
class PedidoController extends Controller
{
// Descarga directa al navegador
public function export()
{
return Excel::download(new PedidosExport, 'pedidos.xlsx');
}
// Guardar en un disco de Laravel (local, s3, etc.)
public function store()
{
Excel::store(new PedidosExport, 'reportes/pedidos.xlsx', 's3');
return back()->with('status', 'Reporte generado');
}
}
Y la ruta correspondiente:
use App\Http\Controllers\PedidoController;
Route::get('/pedidos/export', [PedidoController::class, 'export'])->name('pedidos.export');
Excel::download() fuerza la descarga en el navegador; Excel::store() escribe el archivo en cualquier disco configurado en config/filesystems.php — útil para reportes programados o para enviarlos por email después.
8. Exportar a diferentes formatos (XLSX, CSV)
El mismo export sirve para varios formatos: solo indica la extensión y el tipo de escritor como tercer argumento:
use Maatwebsite\Excel\Excel as ExcelWriter;
// CSV
Excel::download(new PedidosExport, 'pedidos.csv', ExcelWriter::CSV);
// XLSX (por defecto si no indicas nada)
Excel::download(new PedidosExport, 'pedidos.xlsx', ExcelWriter::XLSX);
Si lo que necesitas es una conversión rápida de datos JSON a CSV sin montar todo un export en Laravel, puedes usar mi conversor de JSON a CSV para pruebas puntuales.
9. Exportaciones grandes: procesamiento en cola
Aquí está la diferencia entre un tutorial y código de producción. Exportar 100 mil filas de forma síncrona agota la memoria y da timeout. La solución es doble: usar FromQuery (que ya procesa en chunks) e implementar ShouldQueue para mover el trabajo a un worker en segundo plano.
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Illuminate\Contracts\Queue\ShouldQueue;
class PedidosExport implements FromQuery, WithHeadings, ShouldQueue
{
// ...
}
Y lo lanzas a la cola con queue():
public function export()
{
(new PedidosExport)->queue('reportes/pedidos.xlsx', 's3');
return back()->with('status', 'El reporte se está generando; te avisaremos cuando esté listo.');
}
El paquete divide la exportación en jobs que se procesan de forma independiente, manteniendo el uso de memoria bajo control. Combínalo con colas de Laravel sobre Redis y notifica al usuario por email o broadcasting cuando el archivo esté listo en el disco.
10. Múltiples hojas en un mismo archivo
Para reportes complejos —por ejemplo, una hoja por mes o por sucursal— implementa WithMultipleSheets:
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
class ReporteAnualExport implements WithMultipleSheets
{
public function sheets(): array
{
$sheets = [];
foreach (range(1, 12) as $mes) {
$sheets[] = new PedidosPorMesSheet($mes);
}
return $sheets;
}
}
Cada elemento del array es a su vez una clase de exportación (que puede implementar WithTitle para nombrar la pestaña). Así generas un único .xlsx con doce hojas de un solo download().
Conclusión
Exportar a Excel en Laravel 13 es directo con maatwebsite/excel: creas una clase de exportación, eliges la fuente de datos adecuada (FromQuery para volúmenes grandes), añades encabezados y estilos con los concerns, y disparas la descarga desde el controlador. La clave para producción es no exportar nunca de forma síncrona datasets grandes: FromQuery + ShouldQueue mantiene tu app estable con cualquier volumen.
Para seguir construyendo tu panel de administración:
- Cómo importar archivos Excel en Laravel — la operación inversa, con validación y chunking.
- Cómo optimizar el rendimiento de tu proyecto Laravel — para que tus colas y exportaciones vuelen con Octane y FrankenPHP.
- Cómo crear una API REST con Laravel — expón tus reportes como endpoints.