View Transitions API en App Router de Next.js
La View Transitions API ofrece una forma sencilla de transicionar cualquier cambio visual del DOM de un estado a otro. Esto puede incluir pequeños cambios de contenido como alternar algún contenido, o cambios más amplios como navegar de una página a otra.
Este artículo se centra en mostrar cómo puedes utilizarlo en tu aplicación web con Next.js usando App Router.
Cómo usar View Transitions API en App Router de Next.js
Next.js en su version 14.2 no soporta la View Transitions API, pero puedes implementarla tu mismo de forma muy sencilla.
Si lo quieres hacer tú mismo, puedes seguir los siguientes pasos:
- Utiliza un contexto de React que gestione el final de la transición y utilizarlo en el
layout.tsx
.
'use client'
import { createContext, use, useEffect, useState } from 'react'
import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'
type ViewTransitionsContextValue = (() => void) | null
type ViewTransitionsContextType = Dispatch<
SetStateAction<ViewTransitionsContextValue>
>
const ViewTransitionsContext = createContext<ViewTransitionsContextType>(
() => () => {},
)
export function ViewTransitionsApiProvider({ children }: PropsWithChildren) {
const [finishViewTransition, setFinishViewTransition] =
useState<ViewTransitionsContextValue>(null)
useEffect(() => {
if (finishViewTransition) {
finishViewTransition()
setFinishViewTransition(null)
}
}, [finishViewTransition])
return (
<ViewTransitionsContext.Provider value={setFinishViewTransition}>
{children}
</ViewTransitionsContext.Provider>
)
}
export function useSetFinishViewTransition() {
return use(ViewTransitionsContext)
}
- Crea una abstracción de
next/link
para poder dar soporte a la transición cuando se navega.
import { startTransition } from 'react'
import NextLink from 'next/link'
import { useRouter } from 'next/navigation'
import { useSetFinishViewTransition } from './viewTransitionsApiContext'
// From https://github.com/vercel/next.js/blob/v14.2.2/packages/next/src/client/link.tsx#L180C1-L191C2
function isModifiedEvent(event: React.MouseEvent): boolean {
const eventTarget = event.currentTarget as HTMLAnchorElement | SVGAElement
const target = eventTarget.getAttribute('target')
return (
(target && target !== '_self') ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
event.altKey || // triggers resource download
(event.nativeEvent && event.nativeEvent.which === 2)
)
}
// From https://github.com/vercel/next.js/blob/v14.2.2/packages/next/src/client/link.tsx#L204-L217
function shouldPreserveDefault(
e: React.MouseEvent<HTMLAnchorElement>,
): boolean {
const { nodeName } = e.currentTarget
// anchors inside an svg have a lowercase nodeName
const isAnchorNodeName = nodeName.toUpperCase() === 'A'
if (isAnchorNodeName && isModifiedEvent(e)) {
// ignore click for browser’s default behavior
return true
}
return false
}
export function NextLinkWithTransition(
props: React.ComponentProps<typeof NextLink>,
) {
const router = useRouter()
const finishViewTransition = useSetFinishViewTransition()
const { href, replace, scroll } = props
const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
if (props.onClick) {
props.onClick(e)
}
if ('startViewTransition' in document) {
if (shouldPreserveDefault(e)) {
return
}
e.preventDefault()
document.startViewTransition(
() =>
new Promise<void>((resolve) => {
startTransition(() => {
router[replace ? 'replace' : 'push'](href as string, {
scroll: scroll ?? true,
})
finishViewTransition(() => resolve)
})
}),
)
}
}
return <NextLink {...props} onClick={onClick} />
}
- Define las transiciones con CSS e impórtalo en el
layout.tsx
.
.vt-article-title {
view-transition-name: article-title;
}
Te dejo los enlaces a la implementación de estos pasos en mi aplicación web:
A esta implementación le falta soporte para las navegaciones del navegador, como el botón de atrás o adelante.
En caso de que prefieras delegar esta tarea a una librería, puedes utilizar el paquete next-view-transitions, que es en la que me he basado para hacer esta implementación.
Soporte en otros frameworks o librerías
- React Router le da soporte experimental desde la version 6.17.0
- Remix le da soporte experimental desde la version 2.1.0
- Astro le da soporte desde la version 2.10.0
- Nuxt.js le da soporte desde la version 3
- SvelteKit le da soporte desde la version 1.24.0
Aprende más sobre View Transitions API
Si quieres profundizar más en la View Transitions API, te recomiendo algunos recursos: