GioJSdocs
On this page

useRouter

Navigate from code: push, replace, back, forward, refresh the current page in place, or prefetch a page ahead of time.

tsx
import { useRouter } from '@gio.js/react';

export function CloseButton() {
  const router = useRouter();
  return <button onClick={() => router.back()}>Close</button>;
}

Reference

useRouter() takes no parameters and returns a GioRouter: one frozen object, the same on every render and in every component.

FieldTypeDefaultDescription
push(href, options?)Promise<void>-Navigate to href, adding a history entry.
replace(href, options?)Promise<void>-Navigate to href, replacing the current history entry.
back()void-Go back one entry (history.back()). The router renders that page and restores its scroll position.
forward()void-Go forward one entry (history.forward()).
refresh()Promise<void>-Fetch the current page again and render it in place: same URL, scroll position and component state, fresh props.
prefetch(href)void-Fetch a page into the prefetch cache, as a hovered GioLink does.

Options

The second argument of push and replace (the type RouterNavigateOptions):

OptionTypeDefaultDescription
scrollbooleantrueScroll to the top of the new page, or to the element its #hash names. false keeps the position.
transition'fade' | 'slide-left' | 'slide-up' | 'scale' | falsefalseRun the swap in a view transition, like <GioLink transition>.

push and replace

  • href is resolved against the current URL, so a path (/posts/2), a query alone (?page=2) and a hash (#comments) all work.
  • A same-origin page is fetched (or taken from a fresh prefetch) and rendered in place, then the router scrolls and moves focus. A hash on the current page only scrolls.
  • Another origin, an answer that is not a GioJS page, a network failure or a new deployment becomes a full page load (location.assign, or location.replace for replace).
  • A URL that is not http: or https: (javascript:, mailto:, data:) is refused: the promise rejects with a TypeError, so router.push(userInput) can never run script.
  • The promise resolves once the new page is on screen, or once a full load has started. A navigation overtaken by a newer one resolves without rendering anything.

refresh

router.refresh() empties the prefetch cache and fetches the current URL with cache: 'no-cache', so getServerSideProps runs again (or the server's page cache answers, for a page with revalidate). The answer is rendered into the same React root: layouts and the page keep their state, the scroll position and the history entry stay. When the answer is not a GioJS page, the browser reloads.

prefetch

prefetch(href) fetches a same-origin page into the cache the next navigation to it uses (30 seconds, at most 50 pages). It does nothing for other origins, for the current page and for a page already cached, and its request counts against the server's prefetch budget.

Examples

app/posts/new/editor.tsx
import { useState } from 'react';
import { href, useRouter } from '@gio.js/react';

export function Editor() {
  const router = useRouter();
  const [title, setTitle] = useState('');

  async function save() {
    const res = await fetch('/api/posts', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ title }),
    });
    const { id } = (await res.json()) as { id: string };
    await router.push(href('/posts/:id', { id }));
  }

  return (
    <>
      <input value={title} onChange={(e) => setTitle(e.target.value)} />
      <button onClick={save}>Publish</button>
    </>
  );
}

Refreshing data after a change

app/inbox/mark-all-read.tsx
import { useRouter } from '@gio.js/react';

export function MarkAllRead() {
  const router = useRouter();
  async function markAll() {
    await fetch('/api/inbox/read', { method: 'POST' });
    await router.refresh(); // getServerSideProps runs again; state and scroll stay
  }
  return <button onClick={markAll}>Mark all as read</button>;
}

Prefetching the likely next page

Fetch the page a visitor will most likely open next once this one is on screen, so the click renders it without waiting.

app/cart/checkout-button.tsx
import { useEffect } from 'react';
import { GioLink, useRouter } from '@gio.js/react';

export function CheckoutButton() {
  const router = useRouter();
  useEffect(() => {
    router.prefetch('/checkout');
  }, [router]);
  return <GioLink href="/checkout" prefetch={false}>Checkout</GioLink>;
}

Paging without scrolling

app/posts/pager.tsx
import { useRouter, useSearchParams } from '@gio.js/react';

export function Pager() {
  const router = useRouter();
  const page = Number(useSearchParams().get('page') ?? '1');
  return (
    <button onClick={() => void router.push(`?page=${page + 1}`, { scroll: false })}>
      Next page
    </button>
  );
}

Good to know

  • Every navigation asks the server for the page (unless a fresh prefetch has it). There is no "shallow" mode that changes the URL without fetching.
  • The router holds no URL state: read usePathname, useParams and useSearchParams.
  • During server rendering every method does nothing (push resolves at once). Call them from event handlers and effects.
  • Any same-origin fetch() other than GET, HEAD or OPTIONS empties the prefetch cache, so a navigation after a mutation never shows a page fetched before it.
  • Outside components, use navigate(href, options), which also takes replace.

Version history

VersionChanges
v0.1.0-beta.8Introduced: push, replace, back, forward, refresh and prefetch.