Cómo exportar archivos Excel en Laravel 13: guía completa con Laravel Excel
Tabla de Contenidos
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.
¿maatwebsite/excel es compatible con Laravel 13?
Sí. No hay una “versión para Laravel 13” del paquete: la misma rama 3.1 de siempre lo soporta. Desde la 3.1.69 (abril de 2026) declara compatibilidad con illuminate/support hasta la 13.x, así que en un proyecto Laravel 13 el composer require anterior instala sin fricción. Si estás subiendo desde Laravel 12, tus clases de exportación siguen funcionando tal cual (la API no cambió); solo actualiza con composer update maatwebsite/excel --with-all-dependencies, y ante cualquier conflicto usa composer why-not laravel/framework 13.0 para ver qué paquete estorba.
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.

Así queda el archivo: encabezados en negrita, columna C con formato de moneda y anchos automáticos — todo declarado en la clase de exportación
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.