Índice de contenido
- Características y atributos principales
- Ejemplo
- Métodos
- Header
- Remplazar la pila al navegar
- El Enfoque Moderno: Laravel + Wayfinder para las rutas
- El Generador de Rutas (Los archivos "raros")
- Objeto router y preserveScroll
- Ejemplo NO recomendado (aunque útil como último recurso):
- ✅ Solución real: usar los Callbacks del router
- ✅ Ejemplo real: eliminar un TODO y actualizar la lista sin recargar
- ¿Qué es page?
- ✅ Preservar el scroll
- Notas importantes sobre tu implementación
- ⭐ Conclusión y recomendación final
- Opción de Eliminar en CRUD
- Sobre InertiaLink
- Resolver links is discouraged as it causes "Open Link in New Tab/Window"
- ✔ Solución recomendada por Inertia
- Conclusiones
Para crear enlaces dentro de una aplicación de Inertia lo cual es ideal para nuestros formularios con Drag and Drop, deberá usar el componente de enlace de Inertia. Este es un envoltorio ligero alrededor de un enlace de anclaje estándar <a> que intercepta eventos de clic y evita que se produzcan recargas de página completa. Así es como Inertia proporciona una experiencia de aplicación de una sola página.
En Inertia.js, el componente <Link> es un contenedor ligero alrededor de una etiqueta de anclaje HTML estándar <a>. Intercepta los eventos de clic para evitar recargas completas de la página, realizando en su lugar solicitudes XHR/fetch en segundo plano al servidor para intercambiar componentes de la página, lo que permite una experiencia de aplicación de una sola página (SPA) fluida.
Características y atributos principales
Puede personalizar el comportamiento de sus enlaces de Inertia pasando propiedades específicas:
href: especifica la URL o ruta de destino (por ejemplo, <Link href="/dashboard">Panel de control</Link>).
method: cambia el verbo HTTP del predeterminado GET a POST, PUT, PATCH o DELETE.
as: cambia el elemento HTML renderizado de una etiqueta <a> a otra cosa, como un <button>.
preserve-scroll: evita que la página se restablezca al principio cuando se hace clic, manteniendo su posición de desplazamiento actual.
preserve-state: mantiene intacto el estado local de un componente de página (como las entradas de formulario no enviadas) sin volver a renderizarlo por completo.
data: le permite enviar objetos de datos o cargas útiles específicos junto con la solicitud.
Ejemplo
En una aplicación en Vue, simplemente reemplace sus etiquetas <a> estándar con el componente <Link> de Inertia:
// Vue 3 Example
<script setup>
import { Link } from '@inertiajs/vue3'
</script>
<template>
<Link href="/profile" method="post" as="button" preserve-scroll>
Update Profile
</Link>
</template>Métodos
Puede especificar el método para una solicitud de vínculo de inercia. El valor predeterminado es GET, pero también puede usar POST, PUT, PATCH y DELETE.
import { Link } from '@inertiajs/vue3'
<Link href="/logout" method="post">Logout</Link>Header
Al igual que incluir headers:
import { Link } from '@inertiajs/vue3'
<Link href="/endpoint" :headers="{ foo: bar }">Save</Link>Remplazar la pila al navegar
Puede especificar el comportamiento del historial del navegador. Por defecto, las visitas a la página insertan el estado (nuevo) (window.history.pushState) en el historial; sin embargo, también es posible reemplazar el estado (window.history.replaceState) configurando el atributo replace en verdadero. Esto hará que la visita reemplace el estado de historial actual, en lugar de agregar un nuevo estado de historial a la pila:
import { Link } from '@inertiajs/vue3'
<Link href="/" replace>Home</Link>El Enfoque Moderno: Laravel + Wayfinder para las rutas
Ahora vamos a conocer el esquema más actual utilizando Wayfinder. Aunque al principio parezca un poco complejo, su objetivo es automatizar por completo la comunicación entre Laravel y Vue.
Antiguamente, dependíamos de paquetes externos (como ziggy-js) para tener una función route() que autocompletara las rutas. Ahora, con el nuevo ecosistema de Inertia, el enfoque es generar archivos de TypeScript que "mapean" nuestras rutas de PHP directamente a funciones de JavaScript.
El Generador de Rutas (Los archivos "raros")
Si revisas tu proyecto, verás archivos generados automáticamente que parecen ilegibles. Estos archivos son el resultado de un comando que escanea tus controladores de Laravel y crea funciones listas para usar en Vue.
resources\js\routes\category\index.ts
export const index = (options?: RouteQueryOptions): RouteDefinition<'get'> => ({
url: index.url(options),
method: 'get',
})
***
export const store = (options?: RouteQueryOptions): RouteDefinition<'post'> => ({
url: store.url(options),
method: 'post',
})Si no se generan solos, puedes lanzarlo manualmente (aunque te advierto que a veces en Windows da errores de caché):
$ php artisan wayfinder:generateEste comando genera un mapa de acciones (Actions). Por ejemplo, para nuestro CategoryController, creará una función store() que ya sabe que debe ir por el método POST y a qué URL específica apuntar.
Aunque usualmente crea estos archivos automáticamente al registrar las rutas de los controladores.
Al usar Wayfinder junto con el componente Link, simplemente puede pasar el objeto resultante directamente a la propiedad href. Link inferirá el método HTTP y la URL directamente del objeto Wayfinder.
import { Link } from '@inertiajs/vue3' import { show } from 'App/Http/Controllers/UserController' <Link :href="show(10)">Andrew</Link>
Objeto router y preserveScroll
Cuando trabajamos con formularios u operaciones CRUD en Laravel + Inertia, es común encontrarnos con problemas de sincronización, especialmente cuando queremos actualizar la interfaz sin tener que recurrir a un window.location.reload(), que aunque “funciona”, es una mala práctica y además causa inconsistencias.
Ejemplo NO recomendado (aunque útil como último recurso):
create() {
this.todoSelected = 0
router.post(route('todo.store'), { name: this.form.name })
setTimeout(() => window.location.reload(), 500) // ❌ Horrible pero funcional
}El problema es que ese timeOut no está sincronizado con la operación real, así que puede recargar demasiado rápido o demasiado tarde.
✅ Solución real: usar los Callbacks del router
Inertia provee callbacks que se ejecutan dependiendo del estado de la petición:
- Callback Cuándo se ejecuta
- onBefore Antes de iniciar la visita
- onStart Cuando Inertia empieza a procesar
- onProgress Durante la subida de archivos
- onSuccess Cuando la petición fue exitosa
- onError Cuando hay errores de validación o server
- onFinish Siempre que finaliza (éxito o error)
Sintaxis general:
router.<method>(url, data, {
onBefore: visit => {},
onStart: visit => {},
onProgress: progress => {},
onSuccess: page => {},
onError: errors => {},
onFinish: visit => {},
})✅ Ejemplo real: eliminar un TODO y actualizar la lista sin recargar
Tú implementaste correctamente el callback onSuccess.
La lógica queda así:
router.delete(route('todo.destroy', this.deleteTodoRow), {
onSuccess: (page) => {
console.log(page.props.todos)
this.dtodos = page.props.todos
},
})¿Qué es page?
Es un objeto que contiene:
- component
- props
- url
- version
Dentro de page.props vienen los mismos datos que envías desde el servidor.
En tu caso:
page.props.todoscontiene la lista de TODOS actualizada, que puedes reasignar directamente al estado del componente (this.dtodos).
Esto elimina la necesidad de recargar manualmente la página.
✅ Preservar el scroll
Otro problema típico: el scroll "salta" al inicio al actualizar la página virtual de Inertia.
Solución:
router.delete(route('todo.destroy', this.deleteTodoRow), {
preserveScroll: true,
onSuccess: (page) => {
this.dtodos = page.props.todos
},
})Con preserveScroll: true, la interfaz mantiene su posición exacta en pantalla.
Notas importantes sobre tu implementación
✔ Lo más complicado
Lo más difícil suele ser encajar correctamente:
- la ruta
- los parámetros
- la data
El objeto de opciones con callbacks
Ejemplo correctamente estructurado:
router.put(
route('todo.update', this.todo.id),
{ name: this.form.name }, // ← data
{
preserveScroll: true, // ← opciones
onSuccess: (page) => {
this.dtodos = page.props.todos
}
}
)⭐ Conclusión y recomendación final
Para evitar recargar la página y resolver problemas de sincronización, usa siempre los callbacks del router, especialmente:
- onSuccess → cuando quieres aplicar cambios basados en la respuesta
- onFinish → si quieres ejecutar algo siempre
- preserveScroll → para mantener posición del usuario
Ejemplo final limpio:
router.delete(route('todo.destroy', this.deleteTodoRow), {
preserveScroll: true,
onSuccess: (page) => {
this.dtodos = page.props.todos
},
})Con esto:
- no hay necesidad de setTimeout
- no hay recarga manual
- no se pierde el scroll
- tus datos quedan sincronizados correctamente
Opción de Eliminar en CRUD
Otro buen uso de los Link en Inertia, es el uso para eliminar registros, veamos una implementación.
Vamos a implementar la funcionalidad para eliminar una categoría; colocamos un nuevo enlace usando el componente de Link e indicamos el método de tipo DELETE en el TD de las acciones en el listado.
Como los enlaces (<a>) siempre hacen peticiones GET, debemos indicar explícitamente que queremos enviar una solicitud DELETE.
Esto se hace con la propiedad method="delete":
resources/js/Pages/Dashboard/Category/Index.vue
***
<td class="p-2">
<Link :href="route('category.edit', c.id)">Edit</Link>
<Link method="DELETE" :href="route('category.destroy', c.id)">Delete</Link>
</td>
***- method="delete" → Inertia enviará una petición DELETE.
- as="button" → evita la advertencia en consola y convierte el <Link> en un botón.
- type="button" → opcional, pero recomendado.
Y desde el controlador, es exactamente el mismo código usado en Laravel básico, que es llamar a la función de delete() de Eloquent:
app/Http/Controllers/Dashboard/CategoryController.php
public function destroy(Category $category)
{
$category->delete();
}Debemos registrar la ruta destroy, que recibirá el ID de la categoría a eliminar:
Route::delete('/category/{category}', [CategoryController::class, 'destroy'])
->name('category.destroy');Si vemos en la consola del navegador desde el componente de Index.vue de las categorías, veremos una advertencia como la siguiente:
...links is discouraged as it causes "Open Link in New Tab/Window" accessibility issues. Instead, consider using a more appropriate element, such as a <button>.Tal cual indica, renderizar un enlace para este tipo de operaciones no es lo recomendado ya que, el usuario puede abrir una pestaña en el navegador; para evitar este comportamiento, hay que convertir el enlace a un botón; para ello:
<Link as="button" type="button" method="DELETE" class="text-sm text-red-400 hover:text-red-700 ml-2" :href="route('category.destroy', c.id)">Delete</Link>Sobre InertiaLink
Vamos a hablar sobre un cambio importante que tenemos en las últimas actualizaciones de de Inertia en Laravel, y es un cambio de organización interna de Inertia; específicamente el cambio de nombre de un módulo de Vue:
https://inertiajs.com/releases/inertia-vue3-0.5.0-2021-07-13
Ahora nuestros InertiaLink se llaman como Link:
<inertialink method="DELETE" :href="route('user.destroy', { customer: u })">Borrar</Link>Aparte de esto, tenemos que registrarlo para poder emplearlo
import { Link } from '@inertiajs/vue3' // vue 3o
import { Link } from '@inertiajs/vue' // vue 2
components: {
...
Link
},Y lo podemos emplear sin problemas de la misma manera; en este aspecto, no tenemos ningún cambio:
<Link method="DELETE" :href="route('user.destroy', { customer: u })">Borrar</Link>Resolver links is discouraged as it causes "Open Link in New Tab/Window"
Si revisaste la consola del navegador cuando eliminaste un registro, seguramente viste una advertencia similar a esta:
“Using <Link> for POST, PUT, PATCH or DELETE requests is discouraged.”Esto ocurre porque el componente <Link> de Inertia está pensado para navegación, no para operaciones mutadoras (crear, actualizar, eliminar).
✔ Solución recomendada por Inertia
Convertir el <Link> en un elemento button utilizando la propiedad as, así:
<Link
method="delete"
:href="route('todo.destroy', todo.id)"
as="button"
>
Eliminar
</Link>También puedes agregar type="button" para mayor claridad, aunque no es estrictamente necesario:
<Link
method="delete"
:href="route('todo.destroy', todo.id)"
as="button"
type="button"
>
Eliminar
</Link>Con este cambio:
Ya no aparece la advertencia en consola.
Sigues usando la funcionalidad del <Link> de Inertia.
El elemento se comporta como un botón nativo, que es la forma correcta para operaciones no navegacionales.
Conclusiones
Así que es eso, ahora tenemos un nuevo esquema para trabajar con los enlaces cuyo cambios es SOLO un nombre, para referir nuestras rutas que ahora es mas corto; por lo demás, puedes hacer exactamente lo mismo.
El siguiente paso, aprende a usar los diálogos de confirmación en Laravel Inertia.