> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Paginación

> Explicamos la paginación en Laravel: diferencias entre paginate(), simplePaginate() y cursorPaginate(), visualización en Blade, respuestas API y personalización de URL.

## ¿Qué es la paginación?

La paginación de Laravel se integra con el query builder y Eloquent ORM, y se puede usar sin configuración. La página actual se obtiene automáticamente del parámetro `page` de la solicitud HTTP y se añade también a los enlaces generados.

El HTML por defecto es compatible con Tailwind CSS, y también hay opción para Bootstrap CSS.

## Tres tipos de paginación

| Método             | Devuelve               | Características                                      |
| ------------------ | ---------------------- | ---------------------------------------------------- |
| `paginate()`       | `LengthAwarePaginator` | Cuenta el total. Genera enlaces con número de página |
| `simplePaginate()` | `Paginator`            | No cuenta el total. Solo "anterior/siguiente"        |
| `cursorPaginate()` | `CursorPaginator`      | Basada en cursor. Ideal para grandes volúmenes       |

## Uso básico

### Paginación con query builder

```php theme={null}
use Illuminate\Support\Facades\DB;

// 15 resultados por página
$users = DB::table('users')->paginate(15);
```

### Paginación con Eloquent

```php theme={null}
use App\Models\User;

$users = User::paginate(15);

// Con condiciones
$users = User::where('votes', '>', 100)->paginate(15);
```

### simplePaginate

Cuando no necesitas la consulta de conteo total (solo "anterior/siguiente"), `simplePaginate()` es más eficiente.

```php theme={null}
$users = DB::table('users')->simplePaginate(15);
$users = User::where('active', true)->simplePaginate(15);
```

<Tip>
  Si no necesitas mostrar "X de N en total", elige `simplePaginate()`. `paginate()` ejecuta un `COUNT(*)` adicional, por lo que `simplePaginate()` es más rápido.
</Tip>

### cursorPaginate (paginación por cursor)

En lugar de OFFSET usa WHERE, por lo que ofrece un gran rendimiento con muchos datos. Es especialmente adecuado para interfaces de scroll infinito.

```php theme={null}
// orderBy es obligatorio
$users = DB::table('users')->orderBy('id')->cursorPaginate(15);
$users = User::where('active', true)->cursorPaginate(15);
```

Las URLs contienen un cursor en lugar de un número de página.

```
http://example.com/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0
```

<Warning>
  Para usar cursorPaginate se necesita `orderBy`. Además, la columna de ordenación debe pertenecer a la tabla paginada.
</Warning>

### OFFSET vs. cursor

```sql theme={null}
-- paginate() / simplePaginate() — usa OFFSET
SELECT * FROM users ORDER BY id ASC LIMIT 15 OFFSET 15;

-- cursorPaginate() — usa WHERE (aprovecha los índices)
SELECT * FROM users WHERE id > 15 ORDER BY id ASC LIMIT 15;
```

La paginación por cursor aprovecha los índices y evita mejor la duplicación/omisión de registros cuando hay muchas inserciones/eliminaciones. Sin embargo, no genera enlaces por número de página, solo "anterior/siguiente".

## Implementación en el controlador

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\View\View;

class UserController extends Controller
{
    public function index(): View
    {
        $users = User::orderBy('name')->paginate(20);

        return view('users.index', compact('users'));
    }
}
```

## Mostrar enlaces de paginación en Blade

```blade theme={null}
<div class="container">
    @foreach ($users as $user)
        <p>{{ $user->name }}</p>
    @endforeach
</div>

{{-- Muestra los enlaces (compatibles con Tailwind CSS) --}}
{{ $users->links() }}
```

`links()` genera automáticamente el HTML de los enlaces. Muestra tres páginas a cada lado de la actual.

### Ajustar el número de enlaces

Con `onEachSide()` cambias cuántas páginas se muestran a cada lado.

```blade theme={null}
{{-- Muestra 5 páginas a cada lado --}}
{{ $users->onEachSide(5)->links() }}
```

## Recibir el tamaño de página desde la solicitud

```php theme={null}
public function index(Request $request): View
{
    $perPage = $request->integer('per_page', 15);
    $perPage = min(max($perPage, 1), 100); // Limitar entre 1 y 100

    $users = User::paginate($perPage);

    return view('users.index', compact('users'));
}
```

## Múltiples paginadores en una misma página

Si hay dos paginadores en una misma pantalla, ambos usan `page` y entran en conflicto. Cambia el nombre del parámetro con el tercer argumento.

```php theme={null}
$users = User::paginate(
    perPage: 15,
    columns: ['*'],
    pageName: 'users'
);

$posts = Post::paginate(
    perPage: 10,
    columns: ['*'],
    pageName: 'posts'
);
```

## Personalización de la URL

### Cambiar la base de la URL

```php theme={null}
$users = User::paginate(15);

// URLs del tipo /admin/users?page=N
$users->withPath('/admin/users');
```

### Añadir parámetros de query

```php theme={null}
// Añade sort=votes a cada enlace
$users->appends(['sort' => 'votes']);

// Hereda todos los parámetros de la solicitud actual
$users->withQueryString();
```

### Añadir fragmento hash

```php theme={null}
// Añade #users al final de la URL
$users->fragment('users');
```

## Respuesta API (JSON)

Al devolver un paginador directamente desde una ruta o controlador, se convierte automáticamente a JSON.

```php theme={null}
use App\Models\User;

Route::get('/api/users', function () {
    return User::paginate(15);
});
```

Formato de la respuesta JSON:

```json theme={null}
{
    "total": 50,
    "per_page": 15,
    "current_page": 1,
    "last_page": 4,
    "first_page_url": "http://example.com/api/users?page=1",
    "last_page_url": "http://example.com/api/users?page=4",
    "next_page_url": "http://example.com/api/users?page=2",
    "prev_page_url": null,
    "path": "http://example.com/api/users",
    "from": 1,
    "to": 15,
    "data": [
        { "id": 1, "name": "Taro Yamada" },
        { "id": 2, "name": "Hanako Suzuki" }
    ]
}
```

### Combinación con API Resources

Para envolver el resultado de `paginate()` en una colección de recurso, pásaselo a `UserResource::collection()`.

```php theme={null}
use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/api/users', function () {
    $users = User::paginate(15);

    return UserResource::collection($users);
});
```

Al pasar el paginador a `UserResource::collection()`, la información de paginación se añade automáticamente como metadatos.

<Info>
  El JSON de `cursorPaginate()` incluye `next_cursor` y `prev_cursor` en lugar de números de página. El cliente API usa esos valores como parámetro `cursor` en la siguiente solicitud.
</Info>

## Vistas de paginación personalizadas

### Indicar la vista directamente

```blade theme={null}
{{-- Muestra los enlaces con una vista personalizada --}}
{{ $paginator->links('vendor.pagination.custom') }}

{{-- Pasar datos adicionales --}}
{{ $paginator->links('vendor.pagination.custom', ['theme' => 'dark']) }}
```

### Cambiar la vista por defecto

Primero publica las vistas oficiales para personalizarlas.

```shell theme={null}
php artisan vendor:publish --tag=laravel-pagination
```

Se crean en `resources/views/vendor/pagination/`:

* `tailwind.blade.php` — Por defecto (Tailwind CSS)
* `bootstrap-5.blade.php` — Bootstrap 5
* `simple-tailwind.blade.php` — Para simplePaginate
* ...

Puedes editar `tailwind.blade.php` directamente o crear una nueva vista e indicarla en `AppServiceProvider`.

```php theme={null}
use Illuminate\Pagination\Paginator;

public function boot(): void
{
    Paginator::defaultView('vendor.pagination.custom');
    Paginator::defaultSimpleView('vendor.pagination.simple-custom');
}
```

### Usar Bootstrap CSS

Para usar Bootstrap en lugar de Tailwind, indícalo en el `boot()` de `AppServiceProvider`.

```php theme={null}
use Illuminate\Pagination\Paginator;

public function boot(): void
{
    Paginator::useBootstrapFive(); // Bootstrap 5
    // Paginator::useBootstrapFour(); // Bootstrap 4
}
```

## Crear un paginador manualmente

Para paginar datos existentes (por ejemplo, un array), instancia directamente la clase.

```php theme={null}
use Illuminate\Pagination\LengthAwarePaginator;

$items = collect(range(1, 200))->map(fn ($i) => ['id' => $i, 'name' => "Elemento {$i}"]);

$perPage = 15;
$currentPage = request()->integer('page', 1);

$paginator = new LengthAwarePaginator(
    items: $items->forPage($currentPage, $perPage),
    total: $items->count(),
    perPage: $perPage,
    currentPage: $currentPage,
    options: ['path' => request()->url()]
);
```

## Métodos de instancia habituales

```php theme={null}
$paginator = User::paginate(15);

$paginator->currentPage();     // Página actual
$paginator->lastPage();        // Última página (no en simplePaginate)
$paginator->total();           // Total (no en simplePaginate)
$paginator->perPage();         // Elementos por página
$paginator->count();           // Elementos en la página actual
$paginator->firstItem();       // Índice del primer elemento
$paginator->lastItem();        // Índice del último elemento
$paginator->hasPages();        // ¿Hay más de una página?
$paginator->hasMorePages();    // ¿Hay página siguiente?
$paginator->onFirstPage();     // ¿Estamos en la primera?
$paginator->onLastPage();      // ¿Estamos en la última?
$paginator->nextPageUrl();     // URL de la siguiente
$paginator->previousPageUrl(); // URL de la anterior
$paginator->url(3);            // URL de la página 3
$paginator->items();           // Array de elementos actuales
```

## Resumen

<AccordionGroup>
  <Accordion title="Cómo elegir el tipo de paginación">
    * **`paginate()`** — cuando necesitas total y enlaces numerados (pantallas de listado habituales)
    * **`simplePaginate()`** — cuando basta con "anterior/siguiente" (más rápido)
    * **`cursorPaginate()`** — para grandes volúmenes, scroll infinito o mucha escritura (máximo rendimiento)
  </Accordion>

  <Accordion title="Patrón básico en Blade">
    ```blade theme={null}
    @foreach ($users as $user)
        <p>{{ $user->name }}</p>
    @endforeach

    {{ $users->links() }}
    ```

    Pasa el resultado de `paginate()` a la vista y muestra los enlaces con `links()`.
    La página actual se detecta automáticamente desde el parámetro `page`.
  </Accordion>

  <Accordion title="Paginación en la API">
    Devolver el paginador desde la ruta lo convierte automáticamente a JSON.
    Para combinarlo con API Resources, devuelve `UserResource::collection($paginator)`.
    La respuesta contiene `data` (registros) y varios campos de metadatos.
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel Scout](/es/scout.md)
- [Guía de actualización de Laravel 12 a 13](/es/blog/upgrade-12-to-13.md)
- [API resources de Eloquent](/es/eloquent-resources.md)
- [⚡Introducción a Livewire 4 — Crear UIs reactivas sin JavaScript](/es/blog/livewire-introduction.md)
- [Laravel MCP](/es/mcp.md)
