🍡 mochi
← All demos

Data Loading

Server-side data fetching with the PokéAPI. The Pokémon detail is rendered at request time — pick a different Pokémon from the dropdown to load it via a route param. The stats block hydrates when scrolled into view.

SERVER | PROD

pikachu
#025

pikachu

electric
Height 0.4 m
Weight 6.0 kg
Base XP 112
Abilities static, lightning-rod

Base Stats

hp 35
attack 55
defense 40
special-attack 50
special-defense 50
speed 90
<script>
  import { params } from 'mochi-framework';
  import { pokemonCache } from '../../lib/cache';
  import PokemonHeader from './PokemonHeader.svelte';
  import PokemonMeta from './PokemonMeta.svelte';
  import PokemonStats from './PokemonStats.svelte';
  import PokemonSelector from './PokemonSelector.svelte';

  const id = (params.id ?? 'pikachu').toLowerCase();

  const [pokemon, options] = await Promise.all([
    pokemonCache.fetch(`pokemon:${id}`, async () => {
      const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${encodeURIComponent(id)}`);
      return res.ok ? await res.json() : null;
    }),
    pokemonCache.fetch('pokemon:list:151', async () => {
      const res = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151');
      const data = await res.json();
      return data.results;
    }),
  ]);

  const types = pokemon?.types.map((t) => t.type.name) ?? [];
  const abilities = pokemon?.abilities.map((a) => a.ability.name) ?? [];
  const stats = pokemon?.stats.map((s) => ({ name: s.stat.name, value: s.base_stat })) ?? [];
</script>

<PokemonSelector mochi:hydrate current={id} {options} />

{#if pokemon}
  <div class="card">
    <PokemonHeader id={pokemon.id} name={pokemon.name} sprite={pokemon.sprites.front_default} />
    <div class="divider"></div>
    <PokemonMeta {types} height={pokemon.height} weight={pokemon.weight} baseXp={pokemon.base_experience} {abilities} />
    <div class="divider"></div>
    <PokemonStats mochi:hydrate:visible={{ rootMargin: '100px' }} {stats} />
  </div>
{:else}
  <p class="not-found">No Pokémon found for "{id}".</p>
{/if}
Styles
<style>
  .card {
    background: var(--surface);
    border-radius: var(--radius-lg);
    width: 100%;
    max-width: 320px;
    margin: 0 auto;
    overflow: hidden;
    border: 1px solid var(--border);
  }

  .divider {
    height: 1px;
    background: var(--border);
    margin: 0 1.5rem;
  }

  .not-found {
    text-align: center;
    color: var(--text-muted);
    padding: 2rem 0;
  }
</style>