In Next.js, routes are rendered on the server by default. This often means the client has to wait for a server response before a new route can be shown. Next.js comes with built-in prefetching, streaming, and client-side transitions ensuring navigation stays fast and responsive.
This guide explains how navigation works in Next.js and how you can optimize it for dynamic routes and slow networks.
To understand how navigation works in Next.js, it helps to be familiar with the following concepts:
In Next.js, Layouts and Pages are React Server Components by default. On initial and subsequent navigations, the Server Component Payload is generated on the server before being sent to the client.
There are two types of server rendering, based on when it happens:
The trade-off of server rendering is that the client must wait for the server to respond before the new route can be shown. Next.js addresses this delay by prefetching routes the user is likely to visit and performing client-side transitions.
Good to know: HTML is also generated for the initial visit.
Prefetching is the process of loading a route in the background before the user navigates to it. This makes navigation between routes in your application feel instant, because by the time a user clicks on a link, the data to render the next route is already available client side.
Next.js automatically prefetches routes linked with the <Link> component when they enter the user's viewport.
import Link from 'next/link';
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<nav>
{/* Prefetched when the link is hovered or enters the viewport */}
<Link href="/blog">Blog</Link>
{/* No prefetching */}
<a href="/contact">Contact</a>
</nav>
{children}
</body>
</html>
);
}
import Link from 'next/link';
export default function Layout() {
return (
<html>
<body>
<nav>
{/* Prefetched when the link is hovered or enters the viewport */}
<Link href="/blog">Blog</Link>
{/* No prefetching */}
<a href="/contact">Contact</a>
</nav>
{children}
</body>
</html>
);
}
How much of the route is prefetched depends on whether it's static or dynamic:
loading.tsx is present.By skipping or partially prefetching dynamic routes, Next.js avoids unnecessary work on the server for routes the users may never visit. However, waiting for a server response before navigation can give the users the impression that the app is not responding.
To improve the navigation experience to dynamic routes, you can use streaming.
Streaming allows the server to send parts of a dynamic route to the client as soon as they're ready, rather than waiting for the entire route to be rendered. This means users see something sooner, even if parts of the page are still loading.
For dynamic routes, it means they can be partially prefetched. That is, shared layouts and loading skeletons can be requested ahead of time.
To use streaming, create a loading.tsx in your route folder:
export default function Loading() {
// Add fallback UI that will be shown while the route is loading.
return <LoadingSkeleton />;
}
export default function Loading() {
// Add fallback UI that will be shown while the route is loading.
return <LoadingSkeleton />;
}
Behind the scenes, Next.js will automatically wrap the page.tsx contents in a <Suspense> boundary. The prefetched fallback UI will be shown while the route is loading, and swapped for the actual content once ready.
Good to know: You can also use
<Suspense>to create loading UI for nested components.
Benefits of loading.tsx:
To further improve the navigation experience, Next.js performs a client-side transition with the <Link> component.
Traditionally, navigation to a server-rendered page triggers a full page load. This clears state, resets scroll position, and blocks interactivity.
Next.js avoids this with client-side transitions using the <Link> component. Instead of reloading the page, it updates the content dynamically by:
Client-side transitions are what makes a server-rendered apps feel like client-rendered apps. And when paired with prefetching and streaming, it enables fast transitions, even for dynamic routes.
These Next.js optimizations make navigation fast and responsive. However, under certain conditions, transitions can still feel slow. Here are some common causes and how to improve the user experience:
loading.tsxWhen navigating to a dynamic route, the client must wait for the server response before showing the result. This can give the users the impression that the app is not responding.
We recommend adding loading.tsx to dynamic routes to enable partial prefetching, trigger immediate navigation, and display a loading UI while the route renders.
export default function Loading() {
return <LoadingSkeleton />;
}
export default function Loading() {
return <LoadingSkeleton />;
}
Good to know: In development mode, you can use the Next.js Devtools to identify if the route is static or dynamic. See
devIndicatorsfor more information.
generateStaticParamsIf a dynamic segment could be prerendered but isn't because it's missing generateStaticParams, the route will fallback to dynamic rendering at request time.
Ensure the route is statically generated at build time by adding generateStaticParams:
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json());
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
// ...
}
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
export default async function Page({ params }) {
const { slug } = await params
// ...
}
On slow or unstable networks, prefetching may not finish before the user clicks a link. This can affect both static and dynamic routes. In these cases, the loading.js fallback may not appear immediately because it hasn't been prefetched yet.
To improve perceived performance, you can use the useLinkStatus hook to show immediate feedback while the transition is in progress.
'use client';
import { useLinkStatus } from 'next/link';
export default function LoadingIndicator() {
const { pending } = useLinkStatus();
return <span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />;
}
'use client';
import { useLinkStatus } from 'next/link';
export default function LoadingIndicator() {
const { pending } = useLinkStatus();
return <span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />;
}
You can "debounce" the hint by adding an initial animation delay (e.g. 100ms) and starting as invisible (e.g. opacity: 0). This means the loading indicator will only be shown if the navigation takes longer than the specified delay. See the useLinkStatus reference for a CSS example.
Good to know: You can use other visual feedback patterns like a progress bar. View an example here.
You can opt out of prefetching by setting the prefetch prop to false on the <Link> component. This is useful to avoid unnecessary usage of resources when rendering large lists of links (e.g. an infinite scroll table).
<Link prefetch={false} href="/blog">
Blog
</Link>
However, disabling prefetching comes with trade-offs:
To reduce resource usage without fully disabling prefetch, you can prefetch only on hover. This limits prefetching to routes the user is more likely to visit, rather than all links in the viewport.
'use client';
import Link from 'next/link';
import { useState } from 'react';
function HoverPrefetchLink({ href, children }: { href: string; children: React.ReactNode }) {
const [active, setActive] = useState(false);
return (
<Link href={href} prefetch={active ? null : false} onMouseEnter={() => setActive(true)}>
{children}
</Link>
);
}
'use client';
import Link from 'next/link';
import { useState } from 'react';
function HoverPrefetchLink({ href, children }) {
const [active, setActive] = useState(false);
return (
<Link href={href} prefetch={active ? null : false} onMouseEnter={() => setActive(true)}>
{children}
</Link>
);
}
<Link> is a Client Component and must be hydrated before it can prefetch routes. On the initial visit, large JavaScript bundles can delay hydration, preventing prefetching from starting right away.
React mitigates this with Selective Hydration and you can further improve this by:
@next/bundle-analyzer plugin to identify and reduce bundle size by removing large dependencies.Next.js allows you to use the native window.history.pushState and window.history.replaceState methods to update the browser's history stack without reloading the page.
pushState and replaceState calls integrate into the Next.js Router, allowing you to sync with usePathname and useSearchParams.
window.history.pushStateUse it to add a new entry to the browser's history stack. The user can navigate back to the previous state. For example, to sort a list of products:
'use client';
import { useSearchParams } from 'next/navigation';
export default function SortProducts() {
const searchParams = useSearchParams();
function updateSorting(sortOrder: string) {
const params = new URLSearchParams(searchParams.toString());
params.set('sort', sortOrder);
window.history.pushState(null, '', `?${params.toString()}`);
}
return (
<>
<button onClick={() => updateSorting('asc')}>Sort Ascending</button>
<button onClick={() => updateSorting('desc')}>Sort Descending</button>
</>
);
}
'use client';
import { useSearchParams } from 'next/navigation';
export default function SortProducts() {
const searchParams = useSearchParams();
function updateSorting(sortOrder) {
const params = new URLSearchParams(searchParams.toString());
params.set('sort', sortOrder);
window.history.pushState(null, '', `?${params.toString()}`);
}
return (
<>
<button onClick={() => updateSorting('asc')}>Sort Ascending</button>
<button onClick={() => updateSorting('desc')}>Sort Descending</button>
</>
);
}
window.history.replaceStateUse it to replace the current entry on the browser's history stack. The user is not able to navigate back to the previous state. For example, to switch the application's locale:
'use client';
import { usePathname } from 'next/navigation';
export function LocaleSwitcher() {
const pathname = usePathname();
function switchLocale(locale: string) {
// e.g. '/en/about' or '/fr/contact'
const newPath = `/${locale}${pathname}`;
window.history.replaceState(null, '', newPath);
}
return (
<>
<button onClick={() => switchLocale('en')}>English</button>
<button onClick={() => switchLocale('fr')}>French</button>
</>
);
}
'use client';
import { usePathname } from 'next/navigation';
export function LocaleSwitcher() {
const pathname = usePathname();
function switchLocale(locale) {
// e.g. '/en/about' or '/fr/contact'
const newPath = `/${locale}${pathname}`;
window.history.replaceState(null, '', newPath);
}
return (
<>
<button onClick={() => switchLocale('en')}>English</button>
<button onClick={() => switchLocale('fr')}>French</button>
</>
);
}
How navigation works
정적/동적 렌더링 버튼을 눌러보세요. 렌더링이 언제 발생하는지, 결과가 캐시되는지, 그리고 응답 지연이 어떻게 달라지는지 즉시 확인할 수 있습니다. 이것이 Next.js가 prefetching으로 동적 렌더링의 지연을 해소하려는 이유입니다.
How navigation works
아래 링크 위에 마우스를 올려보세요. <Link> 모드에서는 hover 시 prefetch 상태가 즉시 바뀝니다. <a>로 전환하면 어떤 링크도 prefetch되지 않는 차이를 확인할 수 있습니다. 이것이 내부 링크에 항상 Link를 써야 하는 이유입니다.
How navigation works
loading.tsx 토글을 켜고 “네비게이션 시뮬레이션” 버튼을 눌러보세요. 없을 때는 콘텐츠가 완전히 준비될 때까지 아무것도 안 보이다가, 있을 때는 로딩 UI가 즉시 나타나는 차이를 확인할 수 있습니다. 이것이 스트리밍이 동적 라우트의 사용자 경험을 개선하는 방식입니다.
How navigation works
레이아웃 카운터를 몇 번 올린 뒤 페이지를 전환해보세요. 클라이언트 전환 모드에서는 카운터가 유지되지만, 전체 새로고침 모드로 바꾸면 전환 시 카운터가 0으로 리셋됩니다. 이것이 <Link>가 전통적인 <a> 태그보다 UX가 뛰어난 이유입니다.
What can make transitions slow?
loading.tsx 체크박스를 끈 상태에서 “/blog/[slug] 로 이동”을 눌러보세요. 서버 응답이 올 때까지 화면이 완전히 멈춥니다. 체크박스를 켜면 스켈레톤 UI가 즉시 나타나 사용자가 기다리는 걸 체감하지 못합니다. 이것이 동적 라우트에 항상loading.tsx를 추가해야 하는 이유입니다.
What can make transitions slow?
generateStaticParams 체크박스를 켜고 끄면서 아래 포스트를 클릭해보세요. 없을 때는 동적 렌더링으로 폴백되어 각 링크의 prefetch 상태가 “건너뜀”으로 표시됩니다. 켜면 빌드 타임에 모든 슬러그를 미리 생성해 즉각 전환됩니다.
What can make transitions slow?
네트워크 속도를 “느림”으로 설정하고 링크를 클릭해보세요. useLinkStatus 없이는 링크를 눌러도 아무 반응이 없어 앱이 멈춘 것처럼 보입니다. 체크박스를 켜면 pending 인디케이터가 즉시 나타나 전환 중임을 알려줍니다. 이것이 느린 네트워크에서도 사용자가 피드백을 받을 수 있는 이유입니다.
What can make transitions slow?
세 가지 prefetch 모드를 선택한 뒤 링크 위에 마우스를 올리거나 “뷰포트 진입 시뮬레이션” 버튼을 눌러보세요. 각 모드에서 언제 prefetch가 발생하는지 즉시 확인할 수 있습니다. 이것이 대용량 링크 목록에서 불필요한 리소스 사용을 줄이는 방법입니다.
What can make transitions slow?
번들 크기 슬라이더를 조절해보세요. 번들이 클수록 hydration이 완료되는 시간이 길어지고, 그만큼 prefetch가 늦게 시작됩니다. 이것이 번들 크기를 줄이는 것이 단순한 로딩 속도 이상으로 중요한 이유입니다 — prefetch 시작 시점 자체가 달라집니다.
Examples
정렬 옵션을 바꿔 pushState를 실행하면 히스토리 스택에 새 항목이 쌓입니다. 언어를 바꿔 replaceState를 실행하면 현재 항목이 교체됩니다. ‘뒤로’ 버튼을 눌러 pushState는 되돌아갈 수 있지만 replaceState는 이전 상태가 없다는 차이를 직접 확인해보세요.
In Next.js, routes are rendered on the server by default. This often means the client has to wait for a server response before a new route can be shown. Next.js comes with built-in prefetching, streaming, and client-side transitions ensuring navigation stays fast and responsive.
This guide explains how navigation works in Next.js and how you can optimize it for dynamic routes and slow networks.
To understand how navigation works in Next.js, it helps to be familiar with the following concepts:
In Next.js, Layouts and Pages are React Server Components by default. On initial and subsequent navigations, the Server Component Payload is generated on the server before being sent to the client.
There are two types of server rendering, based on when it happens:
The trade-off of server rendering is that the client must wait for the server to respond before the new route can be shown. Next.js addresses this delay by prefetching routes the user is likely to visit and performing client-side transitions.
Good to know: HTML is also generated for the initial visit.
Prefetching is the process of loading a route in the background before the user navigates to it. This makes navigation between routes in your application feel instant, because by the time a user clicks on a link, the data to render the next route is already available client side.
Next.js automatically prefetches routes linked with the <Link> component when they enter the user's viewport.
import Link from 'next/link';
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<nav>
{/* Prefetched when the link is hovered or enters the viewport */}
<Link href="/blog">Blog</Link>
{/* No prefetching */}
<a href="/contact">Contact</a>
</nav>
{children}
</body>
</html>
);
}
import Link from 'next/link';
export default function Layout() {
return (
<html>
<body>
<nav>
{/* Prefetched when the link is hovered or enters the viewport */}
<Link href="/blog">Blog</Link>
{/* No prefetching */}
<a href="/contact">Contact</a>
</nav>
{children}
</body>
</html>
);
}
How much of the route is prefetched depends on whether it's static or dynamic:
loading.tsx is present.By skipping or partially prefetching dynamic routes, Next.js avoids unnecessary work on the server for routes the users may never visit. However, waiting for a server response before navigation can give the users the impression that the app is not responding.
To improve the navigation experience to dynamic routes, you can use streaming.
Streaming allows the server to send parts of a dynamic route to the client as soon as they're ready, rather than waiting for the entire route to be rendered. This means users see something sooner, even if parts of the page are still loading.
For dynamic routes, it means they can be partially prefetched. That is, shared layouts and loading skeletons can be requested ahead of time.
To use streaming, create a loading.tsx in your route folder:
export default function Loading() {
// Add fallback UI that will be shown while the route is loading.
return <LoadingSkeleton />;
}
export default function Loading() {
// Add fallback UI that will be shown while the route is loading.
return <LoadingSkeleton />;
}
Behind the scenes, Next.js will automatically wrap the page.tsx contents in a <Suspense> boundary. The prefetched fallback UI will be shown while the route is loading, and swapped for the actual content once ready.
Good to know: You can also use
<Suspense>to create loading UI for nested components.
Benefits of loading.tsx:
To further improve the navigation experience, Next.js performs a client-side transition with the <Link> component.
Traditionally, navigation to a server-rendered page triggers a full page load. This clears state, resets scroll position, and blocks interactivity.
Next.js avoids this with client-side transitions using the <Link> component. Instead of reloading the page, it updates the content dynamically by:
Client-side transitions are what makes a server-rendered apps feel like client-rendered apps. And when paired with prefetching and streaming, it enables fast transitions, even for dynamic routes.
These Next.js optimizations make navigation fast and responsive. However, under certain conditions, transitions can still feel slow. Here are some common causes and how to improve the user experience:
loading.tsxWhen navigating to a dynamic route, the client must wait for the server response before showing the result. This can give the users the impression that the app is not responding.
We recommend adding loading.tsx to dynamic routes to enable partial prefetching, trigger immediate navigation, and display a loading UI while the route renders.
export default function Loading() {
return <LoadingSkeleton />;
}
export default function Loading() {
return <LoadingSkeleton />;
}
Good to know: In development mode, you can use the Next.js Devtools to identify if the route is static or dynamic. See
devIndicatorsfor more information.
generateStaticParamsIf a dynamic segment could be prerendered but isn't because it's missing generateStaticParams, the route will fallback to dynamic rendering at request time.
Ensure the route is statically generated at build time by adding generateStaticParams:
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json());
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
// ...
}
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
export default async function Page({ params }) {
const { slug } = await params
// ...
}
On slow or unstable networks, prefetching may not finish before the user clicks a link. This can affect both static and dynamic routes. In these cases, the loading.js fallback may not appear immediately because it hasn't been prefetched yet.
To improve perceived performance, you can use the useLinkStatus hook to show immediate feedback while the transition is in progress.
'use client';
import { useLinkStatus } from 'next/link';
export default function LoadingIndicator() {
const { pending } = useLinkStatus();
return <span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />;
}
'use client';
import { useLinkStatus } from 'next/link';
export default function LoadingIndicator() {
const { pending } = useLinkStatus();
return <span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />;
}
You can "debounce" the hint by adding an initial animation delay (e.g. 100ms) and starting as invisible (e.g. opacity: 0). This means the loading indicator will only be shown if the navigation takes longer than the specified delay. See the useLinkStatus reference for a CSS example.
Good to know: You can use other visual feedback patterns like a progress bar. View an example here.
You can opt out of prefetching by setting the prefetch prop to false on the <Link> component. This is useful to avoid unnecessary usage of resources when rendering large lists of links (e.g. an infinite scroll table).
<Link prefetch={false} href="/blog">
Blog
</Link>
However, disabling prefetching comes with trade-offs:
To reduce resource usage without fully disabling prefetch, you can prefetch only on hover. This limits prefetching to routes the user is more likely to visit, rather than all links in the viewport.
'use client';
import Link from 'next/link';
import { useState } from 'react';
function HoverPrefetchLink({ href, children }: { href: string; children: React.ReactNode }) {
const [active, setActive] = useState(false);
return (
<Link href={href} prefetch={active ? null : false} onMouseEnter={() => setActive(true)}>
{children}
</Link>
);
}
'use client';
import Link from 'next/link';
import { useState } from 'react';
function HoverPrefetchLink({ href, children }) {
const [active, setActive] = useState(false);
return (
<Link href={href} prefetch={active ? null : false} onMouseEnter={() => setActive(true)}>
{children}
</Link>
);
}
<Link> is a Client Component and must be hydrated before it can prefetch routes. On the initial visit, large JavaScript bundles can delay hydration, preventing prefetching from starting right away.
React mitigates this with Selective Hydration and you can further improve this by:
@next/bundle-analyzer plugin to identify and reduce bundle size by removing large dependencies.Next.js allows you to use the native window.history.pushState and window.history.replaceState methods to update the browser's history stack without reloading the page.
pushState and replaceState calls integrate into the Next.js Router, allowing you to sync with usePathname and useSearchParams.
window.history.pushStateUse it to add a new entry to the browser's history stack. The user can navigate back to the previous state. For example, to sort a list of products:
'use client';
import { useSearchParams } from 'next/navigation';
export default function SortProducts() {
const searchParams = useSearchParams();
function updateSorting(sortOrder: string) {
const params = new URLSearchParams(searchParams.toString());
params.set('sort', sortOrder);
window.history.pushState(null, '', `?${params.toString()}`);
}
return (
<>
<button onClick={() => updateSorting('asc')}>Sort Ascending</button>
<button onClick={() => updateSorting('desc')}>Sort Descending</button>
</>
);
}
'use client';
import { useSearchParams } from 'next/navigation';
export default function SortProducts() {
const searchParams = useSearchParams();
function updateSorting(sortOrder) {
const params = new URLSearchParams(searchParams.toString());
params.set('sort', sortOrder);
window.history.pushState(null, '', `?${params.toString()}`);
}
return (
<>
<button onClick={() => updateSorting('asc')}>Sort Ascending</button>
<button onClick={() => updateSorting('desc')}>Sort Descending</button>
</>
);
}
window.history.replaceStateUse it to replace the current entry on the browser's history stack. The user is not able to navigate back to the previous state. For example, to switch the application's locale:
'use client';
import { usePathname } from 'next/navigation';
export function LocaleSwitcher() {
const pathname = usePathname();
function switchLocale(locale: string) {
// e.g. '/en/about' or '/fr/contact'
const newPath = `/${locale}${pathname}`;
window.history.replaceState(null, '', newPath);
}
return (
<>
<button onClick={() => switchLocale('en')}>English</button>
<button onClick={() => switchLocale('fr')}>French</button>
</>
);
}
'use client';
import { usePathname } from 'next/navigation';
export function LocaleSwitcher() {
const pathname = usePathname();
function switchLocale(locale) {
// e.g. '/en/about' or '/fr/contact'
const newPath = `/${locale}${pathname}`;
window.history.replaceState(null, '', newPath);
}
return (
<>
<button onClick={() => switchLocale('en')}>English</button>
<button onClick={() => switchLocale('fr')}>French</button>
</>
);
}
How navigation works
정적/동적 렌더링 버튼을 눌러보세요. 렌더링이 언제 발생하는지, 결과가 캐시되는지, 그리고 응답 지연이 어떻게 달라지는지 즉시 확인할 수 있습니다. 이것이 Next.js가 prefetching으로 동적 렌더링의 지연을 해소하려는 이유입니다.
How navigation works
아래 링크 위에 마우스를 올려보세요. <Link> 모드에서는 hover 시 prefetch 상태가 즉시 바뀝니다. <a>로 전환하면 어떤 링크도 prefetch되지 않는 차이를 확인할 수 있습니다. 이것이 내부 링크에 항상 Link를 써야 하는 이유입니다.
How navigation works
loading.tsx 토글을 켜고 “네비게이션 시뮬레이션” 버튼을 눌러보세요. 없을 때는 콘텐츠가 완전히 준비될 때까지 아무것도 안 보이다가, 있을 때는 로딩 UI가 즉시 나타나는 차이를 확인할 수 있습니다. 이것이 스트리밍이 동적 라우트의 사용자 경험을 개선하는 방식입니다.
How navigation works
레이아웃 카운터를 몇 번 올린 뒤 페이지를 전환해보세요. 클라이언트 전환 모드에서는 카운터가 유지되지만, 전체 새로고침 모드로 바꾸면 전환 시 카운터가 0으로 리셋됩니다. 이것이 <Link>가 전통적인 <a> 태그보다 UX가 뛰어난 이유입니다.
What can make transitions slow?
loading.tsx 체크박스를 끈 상태에서 “/blog/[slug] 로 이동”을 눌러보세요. 서버 응답이 올 때까지 화면이 완전히 멈춥니다. 체크박스를 켜면 스켈레톤 UI가 즉시 나타나 사용자가 기다리는 걸 체감하지 못합니다. 이것이 동적 라우트에 항상loading.tsx를 추가해야 하는 이유입니다.
What can make transitions slow?
generateStaticParams 체크박스를 켜고 끄면서 아래 포스트를 클릭해보세요. 없을 때는 동적 렌더링으로 폴백되어 각 링크의 prefetch 상태가 “건너뜀”으로 표시됩니다. 켜면 빌드 타임에 모든 슬러그를 미리 생성해 즉각 전환됩니다.
What can make transitions slow?
네트워크 속도를 “느림”으로 설정하고 링크를 클릭해보세요. useLinkStatus 없이는 링크를 눌러도 아무 반응이 없어 앱이 멈춘 것처럼 보입니다. 체크박스를 켜면 pending 인디케이터가 즉시 나타나 전환 중임을 알려줍니다. 이것이 느린 네트워크에서도 사용자가 피드백을 받을 수 있는 이유입니다.
What can make transitions slow?
세 가지 prefetch 모드를 선택한 뒤 링크 위에 마우스를 올리거나 “뷰포트 진입 시뮬레이션” 버튼을 눌러보세요. 각 모드에서 언제 prefetch가 발생하는지 즉시 확인할 수 있습니다. 이것이 대용량 링크 목록에서 불필요한 리소스 사용을 줄이는 방법입니다.
What can make transitions slow?
번들 크기 슬라이더를 조절해보세요. 번들이 클수록 hydration이 완료되는 시간이 길어지고, 그만큼 prefetch가 늦게 시작됩니다. 이것이 번들 크기를 줄이는 것이 단순한 로딩 속도 이상으로 중요한 이유입니다 — prefetch 시작 시점 자체가 달라집니다.
Examples
정렬 옵션을 바꿔 pushState를 실행하면 히스토리 스택에 새 항목이 쌓입니다. 언어를 바꿔 replaceState를 실행하면 현재 항목이 교체됩니다. ‘뒤로’ 버튼을 눌러 pushState는 되돌아갈 수 있지만 replaceState는 이전 상태가 없다는 차이를 직접 확인해보세요.