Tutorial
Dynamic routes

Dynamic routes

Create the Pokémon route

The homepage is ready. We have the full list of Pokémon and they are all links. Great!

Now we want to make those links actually point to a real page. Let’s create the dynamic route.

Create the folder routes/pokemons and then create the two files [pokemon].tsx and [pokemon].rs.

These two will handle every request that points to http://localhost:3000/pokemons/bulbasaur..mew.

Let’s first work on the server side file. Paste into the new [pokemon].rs file the following code:

// src/routes/pokemons/[pokemon].rs
use serde::{Deserialize, Serialize};
use reqwest::Client;
use tuono_lib::{Props, Request, Response};

const POKEMON_API: &str = "https://pokeapi.co/api/v2/pokemon";

#[derive(Debug, Serialize, Deserialize)]
struct Pokemon {
    name: String,
    id: u16,
    weight: u16,
    height: u16,
}

#[tuono_lib::handler]
async fn get_pokemon(req: Request, fetch: Client) -> Response {
    // The param `pokemon` is defined in the route filename [pokemon].rs
    let pokemon = req.params.get("pokemon").unwrap();

    return match fetch.get(format!("{POKEMON_API}/{pokemon}")).send().await {
        Ok(res) => {
            let data = res.json::<Pokemon>().await.unwrap();
            Response::Props(Props::new(data))
        }
        Err(_err) => Response::Props(Props::new("{}"))
    };
}

Then let’s work on the frontend. Paste into the [pokemon].tsx file the following code:

// src/routes/pokemons/[pokemon].tsx
import type { JSX } from 'react'
import type { TuonoProps } from 'tuono'
import { Link } from 'tuono'

import PokemonView from '../../components/PokemonView'

interface Pokemon {
  id: number
  name: string
  weight: number
  height: number
}

export default function PokemonPage({
  isLoading,
  data,
}: TuonoProps<Pokemon>): JSX.Element {
  return (
    <div>
      <Link href="/">Back</Link>

      {isLoading && (
        <>
          <div>Loading...</div>
        </>
      )}

      {data?.id && (
        <>
          <PokemonView pokemon={data} />
        </>
      )}
    </div>
  )
}

The browser should complain that the component PokemonView does not exist. Let’s create it then!

// src/components/PokemonView.tsx
import type { JSX } from 'react'

import styles from './PokemonView.module.css'

interface Pokemon {
  id: number
  name: string
  weight: number
  height: number
}

interface PokemonViewProps {
  pokemon: Pokemon
}

export default function PokemonView({
  pokemon,
}: PokemonViewProps): JSX.Element {
  return (
    <div className={styles.pokemon}>
      <div>
        <h1 className={styles.name}>{pokemon.name}</h1>
        <dl className={styles.spec}>
          <dt className={styles.label}>Weight: </dt>
          <dd>{pokemon.weight}lbs</dd>
        </dl>
        <dl className={styles.spec}>
          <dt className={styles.label}>Height: </dt>
          <dd>{pokemon.height}ft</dd>
        </dl>
      </div>
      <img
        src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${pokemon.id}.png`}
        alt=""
      />
    </div>
  )
}

To have the proper style consider adding a new css module file:

/* src/components/PokemonView.module.css */
.pokemon {
  display: flex;
  justify-content: space-between;
  margin-top: 20px;
}

.name {
  font-size: 50px;
  font-weight: 700;
}

.pokemon img {
  width: 400px;
}

.spec {
  display: flex;
  font-size: 18px;
  margin-top: 10px;
}

.label {
  font-weight: 700;
}
Edit page