Client-side navigation
After the server has rendered the initial page, the client takes over and handles all subsequent navigation. This can be done by using the client components exported by the @lazarv/react-server/navigation
module or by the functions exposed by the useClient
hook from the @lazarv/react-server/client
module. You can use these functions to navigate to a new page, prefetch a page, or refresh the current page.
It's recommended to use the built-in client components for navigation and refresh. These components will automatically start a React transition to render only the parts of the page that are changing. This will improve the user experience by reducing the amount of time the user has to wait for the page to load.
Functions exposed by the useClient
hook are more low-level and should be used only when you need to start a React transition manually.
You can navigate to a new page by using the Link
client component exported by the @lazarv/react-server/navigation
module.
import { Link } from "@lazarv/react-server/navigation";
export default function Home() {
return (
<div>
<Link to="/about">About</Link>
</div>
);
}
You can enable prefetching of the page by passing the prefetch
prop to the Link
component. This will prefetch the page when the user hovers over the link.
import { Link } from "@lazarv/react-server/client";
export default function Home() {
return (
<div>
<Link to="/about" prefetch>
About
</Link>
</div>
);
}
By default, the prefetched page will be cached indefinitely. You can change this behavior by passing the timeout value in the prefetch
prop to the Link
component. The prefetch
prop accepts a number which represents the number of milliseconds the page will be cached for. After the timeout expires, the page will be removed from the cache and the next time the user hover over the link, the page will be prefetched again.
import { Link } from "@lazarv/react-server/navigation";
export default function Home() {
return (
<div>
<Link to="/about" prefetch={5000}>
About
</Link>
</div>
);
}
To programmatically prefetch a page you can use the prefetch
function returned by the useClient
hook.
import { useClient } from "@lazarv/react-server/client";
export default function Home() {
const { prefetch } = useClient();
return (
<div>
<button onMouseOver={() => prefetch("/about")}>About</button>
</div>
);
}
When the user navigates away from a page the router will cache the last page when you specify a rollback
prop on the Link
component. This will allow the user to navigate back to the previous page without having to wait for the server to render the page again. The provided value represents the number of milliseconds the page will be cached for. After the timeout expires, the page will be removed from the cache and the next time the user navigates back to the page, the page will be rendered again by the server.
import { Link } from "@lazarv/react-server/navigation";
export default function Home() {
return (
<div>
<Link to="/about" rollback={5000}>
About
</Link>
</div>
);
}
To programmatically navigate to a new page you can use the navigate
function returned by the useClient
hook. It's recommended to use navigate
in a React transition to render only the parts of the page that are changing.
import { startTransition } from "react";
import { useClient } from "@lazarv/react-server/client";
export default function Home() {
const { navigate } = useClient();
return (
<div>
<button onClick={() => startTransition(async () => navigate("/about"))}>
About
</button>
</div>
);
}
You can refresh the current page by using the <Refresh>
client component exported by the @lazarv/react-server/navigation
module or the refresh
function returned by the useClient
hook from the @lazarv/react-server/client
module.
import { Refresh } from "@lazarv/react-server/navigation";
export default function Home() {
return (
<div>
<Refresh>Click to refresh!</Refresh>
</div>
);
}
To programmatically refresh the current page you can use the refresh
function returned by the useClient
hook. Again, it's recommended to start a React transition for the page refresh.
import { startTransition } from "react";
import { useClient } from "@lazarv/react-server/client";
export default function Home() {
const { refresh } = useClient();
return (
<div>
<button onClick={() => startTransition(async () => refresh())}>
Refresh
</button>
</div>
);
}
You can use the ReactServerComponent
to create an outlet which you can use for sub-page navigation. It's a modern React Server Component driven version of the <iframe>
element. When using a Link
or Refresh
component or programmatically navigating with the navigate
or replace
functions, the client-side router will automatically update the content of the outlet by rendering the result of server-side rendering the outlet. You need to handle the server-side rendering of the outlet yourself using server-side routing and using the useOutlet helper function.
"use client";
import { Link, ReactServerComponent } from "@lazarv/react-server/navigation";
export default function Home() {
return (
<div>
<Link to="/photos/1" target="modal">My photo</Link>
<ReactServerComponent outlet="modal" />
</div>
);
}
There's four types of Link
or Refresh
navigation inside a ReactServerComponent
. Unspecified, target
, local
and root
. When using target
on the Link
or Refresh
component, the navigation will only update the content of the outlet with the same name. When using local
, the navigation will update the content of the parent outlet while URL of the current page is not updated this way. When using root
, the navigation will update the content of the page. When using an unspecified behavior, the navigation will update the content of the page for each outlet if there are any outlets on the page.
"use client";
import { Link, ReactServerComponent } from "@lazarv/react-server/navigation";
export default function Home() {
return (
<div>
<ReactServerComponent outlet="modal">
<Link to="/photos/2" local>My photo</Link>
</ReactServerComponent>
</div>
);
}
When using the ReactServerComponent
with the defer
prop, the content of the outlet will be rendered only when the user navigates to the page that contains the ReactServerComponent
with the defer
prop. This can be useful when you want to defer the rendering of the content of the outlet until the user navigates to the page that contains the ReactServerComponent
with the defer
prop. The content of the outlet will be fetched from the server.
You can also specify the url
prop on the ReactServerComponent
to specify the URL of the content of the outlet. This can be useful when you want to render the content of the outlet from a different URL than the current page.
Using both defer
and url
props on the ReactServerComponent
you can achieve a similar behavior to the iframe
element.
The useClient
hook returns an object with the following properties:
navigate(url: string, options: { rollback?: number })
: A function that navigates to a new page. Therollback
option allows you to cache the current page for a specified amount of time.replace(url: string, options: { rollback?: number })
: A function that replaces the current page with a new page. Therollback
option allows you to cache the current page for a specified amount of time.prefetch(url: string, options: { ttl?: number })
: A function that prefetches a page. Thettl
option allows you to cache the page for a specified amount of time.refresh()
: A function that refreshes the current page.
You can use these functions for programmatic navigation.
import { startTransition } from "react";
import { useClient } from "@lazarv/react-server/client";
export default function Home() {
const { navigate, replace, prefetch, refresh } = useClient();
return (
<div>
<button onClick={() => startTransition(async () => navigate("/about"))}>
About
</button>
<button onClick={() => startTransition(async () => replace("/about"))}>
Replace
</button>
<button onMouseOver={() => prefetch("/about")}>Prefetch</button>
<button onClick={() => startTransition(async () => refresh())}>
Refresh
</button>
</div>
);
}