TanStack Router: Path Parameters & Loader

TanStack Router: Path Parameters & Loader

Path parameters and loaders for this new typesafe router and state manager for React.

Welcome to the second article of a series where we will explore TanStack Router, the new typesafe routing library (and state manager, in some cases) for React.

In the first article we saw how to set up a new project with TanStack Router and how to create a couple of routes. In this article we’ll see how to handle path parameters and how to use a loader to fetch data before rendering a route.

Path Parameters

Here’s a demo of what you’re going to learn in this article. For written instructions, keep reading below.

On your favourite social network, your account url will be somewhere around https://www.example.com/user/yourusername. The yourusername part is a path parameter, and it’s a way to tell the application that you’d like to see the page for that specific user.

Let’s expand the previous example using pokemons as the domain. We want to create a page for each pokemin, obviously using path parameters.

If you didn’t follow the previous article it’s not a problem, you can start from the code on the 01-routes-and-setup branch.

Adding a new route

Let’s create a new file in src/routes/pokemon/$id.ts. You already see the path has a $id in it, which will be replaced in the URLs with our pokemon id.

Reading the path parameter

Now, how can we read the parameter? When we create our route we’re also creating a Route object which we can use to call some hooks, and there’s one exactly called useParams.

Your initial page to check that everything is working might be something like this:

export const Route = createFileRoute('/pokemon/$id')({
  component: Pokemon,
});

function Pokemon() {
  const { id } = Route.useParams();
  return <div>Pokemon {id}</div>;
}

If you now navigate to http://localhost:5173/pokemon/1 you should see “Pokemon 1” on the screen. This already opens up to a lot of possibilities!

Loader

To make things more interesting, let’s say we want to fetch the pokemon data from an API before rendering the page. We can use a loader for that.

Fetching the data

Here’s an example to have an API, this isn’t about TanStack router but only for the sake of the example. You can have on a file src/api/pokemon.ts:

type PokemonDetail = {
  name: string;
  id: number;
  height: number;
  weight: number;
  sprites: {
    front_default: string;
  };
};

export async function getPokemon(id: string): Promise<PokemonDetail> {
  const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
  return await response.json();
}

This gives us a function getPokemon that we can use to fetch the data for a specific pokemon.

Using the loader

One of the parameters of the createFileRoute function is the loader. This is a function that will be called before rendering the route, and it can be used to fetch data. You can update your Route object to include the loader:

import { createFileRoute } from '@tanstack/react-router';
import { getPokemon } from '../../api/pokemon';

export const Route = createFileRoute('/pokemon/$id')({
  component: Pokemon,
  loader: async ({ params }) => await getPokemon(params.id),
});

function Pokemon() {
  const { id } = Route.useParams();
  return <div>Pokemon {id}</div>;
}

If you now refresh your page, you can already see in the network tab that the data is being fetched from the API and using it in the component is as easy as ready the path parameter since Route also exposes a useLoaderData hook.

const pokemon = Route.useLoaderData();

You can now use the pokemon object to render the pokemon data on the screen, here’s an example:

function Pokemon() {
  const { id } = Route.useParams();
  const pokemon = Route.useLoaderData();
  console.log(pokemon);
  return (
    <div>
      <h2>
        ({id}) {pokemon.name}
      </h2>
      <img src={pokemon.sprites.front_default} alt={pokemon.name} />
      <dl>
        <dt>Height</dt>
        <dd>{pokemon.height}</dd>
        <dt>Weight</dt>
        <dd>{pokemon.weight}</dd>
      </dl>
    </div>
  );
}

Navigating to a route with parameters

If you notice, when you try to navigate to a route with parameters, you cannot set an url like /pokemon/1 or /pokemon/${pokemonId} directly. TanStack Router will force you to pass the route definition as /pokemon/$id and then specify the parameter in the params object, for example:

❌ WRONG:

<Link to={`/pokemon/${pokemon.id}`}>{pokemon.name}</Link>

✅ CORRECT:

<Link to={'/pokemon/$id'} params={{ id: pokemon.id }}>
  {pokemon.name}
</Link>

Is it an additional step? Probably yes, but this makes sure you’re passing the correct parameters and you have autocompletion and typesafety while doing so.

Conclusion

In this article we saw how to handle path parameters and how to use a loader to fetch data before rendering a route. This is a very powerful feature that can be used to create dynamic pages and fetch data from an API.

However, sometimes you have many parameters, even structured! You can’t have a strict link with each parameter sequentially, right? That’s why in the next chapter we’re gonna see one of the greatest features of TanStack Router: search parameters (aka query params).

This library allows you to basically handle your application state in the URL as search params, with the huge advantage that you can let your users bookmark an URL or send it to someone else, passing the state of your application.

Some examples? A search page with filters, a form with a lot of fields or a dashboard with many configurations.

As always, the code for this chapter is on the 02-path-parameters branch and stay tuned for the next article!


Watch the full playlist on YouTube 👇


You can find the full code on this repository on the 02-path-parameters. Leave a ⭐️ if you found the demo code useful!

Did you find this article valuable?

Support Leonardo Montini by becoming a sponsor. Any amount is appreciated!