InertiaLink y Link en Laravel Inertia
Índice de contenido
- Métodos
- Header
- Remplazar la pila al navegar
- 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
- 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.
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>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>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>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.
Acepto recibir anuncios de interes sobre este Blog.
Vemos una actualización de como migrar de InertiaLink al nuevo enfoque que implemento la gente de Inertia Laravel.