Navigation
Use the <Link> component to navigate between pages. It renders a plain <a> element and accepts all standard anchor attributes alongside a typed to prop:
import { Link } from '$houdini'
<Link to="/shows">All Shows</Link>Type-Safe URLs
Houdini knows every valid route in your app, so to is type-checked at compile time. For parameterized routes, pass a params object and Houdini interpolates it into the URL at render time:
// static route: TypeScript verifies "/shows" is a real page<Link to="/shows">All Shows</Link>
// parameterized route: TypeScript requires params.id<Link to="/shows/[id]" params={{ id: show.id }}> {show.title}</Link>TypeScript will error if:
torefers to a path that doesn’t exist in your appparamsis missing for a parameterized routeparamscontains the wrong keys or incompatible value types
Param types
Param types come from the GraphQL variables attached to that route. If $id is declared as ID!, the param accepts a string (IDs are opaque values, not numbers). Custom scalars from your Houdini config are reflected too. A DateTime param expects a Date value and will serialize into the url with the function you defined in your config file.
External links
External URLs, fragments, relative paths, and other non-app hrefs are accepted without any special handling:
<Link to="https://example.com">External</Link><Link to="mailto:hello@example.com">Email</Link><Link to="#section">Jump to section</Link><Link to="./sibling">Relative</Link>Search Params
The search prop configures the query string of a Link. Pass an object and Houdini will serialize it into the URL:
// → /shows?genre=comedy<Link to="/shows" search={{ genre: 'comedy' }}>Comedies</Link>The route’s nullable query variables show up as typed keys, so a mistyped value (a string where the query wants an Int) is a compile error rather than a silently empty result. search isn’t limited to them, though: extra keys are allowed so the query string can also hold UI-only state that no query reads (a selected tab, an open modal). A list variable accepts an array and serializes as repeated keys:
// → /shows?tag=comedy&tag=drama<Link to="/shows" search={{ tags: ['comedy', 'drama'] }}>Both</Link>A search param backed by a custom scalar is marshaled into the URL the same way a path param is, so a DateTime accepts a Date and serializes with your config’s marshal function. On the read side, useRoute().search unmarshals it back to its runtime type (a Date). One caveat: because the URL is string-only, values are decoded with JSON.parse before unmarshaling, so a custom scalar whose value is "true" or "123" comes back as a boolean or number.
These params aren’t just decoration on the URL. They flow straight back into the route’s query, and changing them re-runs it, so a <Link> that swaps ?genre=comedy for ?genre=drama refetches the page with the new filter. See Search Params for the data side of the story.
goto accepts the same typed target as <Link> (a to route plus its params and search) and builds the URL for you, with the same compile-time checks:
// → /shows?genre=comedygoto({ to: '/shows', search: { genre: 'comedy' } })
// → /shows/123goto({ to: '/shows/[id]', params: { id: show.id } })It also accepts a ready-made URL string as an escape hatch, which works the same way for query strings as it does for paths. Pass the whole URL, search string included:
goto(`/shows?genre=${encodeURIComponent(genre)}`)Disabled
Pass disabled to prevent navigation. The href attribute is omitted so the element is inert, and you can add a class to style it:
<Link to="/[[id]]" params={{ id: id - 1 }} disabled={id <= 1} className={id <= 1 ? 'disabled' : undefined}> previous</Link>Preloading
Add preload to a <Link> and Houdini will begin fetching when the user hovers, before they click:
<Link to="/shows" preload>All Shows</Link>By default this fetches both the page component and its data. You can narrow it:
"data": only the GraphQL data"component": only the JavaScript bundle"page": both (default)
<Link to="/shows" preload="data">All Shows</Link>Imperative Navigation
When you need to navigate in response to something other than a click (after a form submission, inside an effect, or from a callback), call goto from useRoute:
import type { PageRoute } from './$types'import { useRoute } from '$houdini'
export function SearchForm() { const { goto } = useRoute<PageRoute>()
function handleSubmit(e: React.FormEvent<HTMLFormElement>) { e.preventDefault() const query = new FormData(e.currentTarget).get('q') goto({ to: '/search', search: { q: query as string } }) }
return ( <form onSubmit={handleSubmit}> <input name="q" /> <button type="submit">Search</button> </form> )}import { useRoute } from '$houdini'
export function SearchForm() { const { goto } = useRoute()
function handleSubmit(e) { e.preventDefault() const query = new FormData(e.currentTarget).get('q') goto({ to: '/search', search: { q: query } }) }
return ( <form onSubmit={handleSubmit}> <input name="q"/> <button type="submit">Search</button> </form>)}useRoute also exposes pathname, params, and search if you need to read the current URL, for example to mark a link as active:
import type { PageRoute } from './$types'import { useRoute } from '$houdini'
export function NavLink({ href, label }: { href: string; label: string }) { const { pathname } = useRoute<PageRoute>() return ( <a href={href} aria-current={pathname === href ? 'page' : undefined}> {label} </a> )}See useRoute for the full API.