Command Palette

Search for a command to run...

Build a Pokédex

A progressive guide: build a complete Pokédex with compodex primitives, step by step.

This guide builds a complete Pokédex — list, search, pagination, empty state, and filters — using the Pokedex primitives. Each step adds one capability on top of the previous one, the same way a feature grows in a real project.

Note

The primitives manage state and composition. Data fetching, caching, and indexing are always your app's responsibility — this guide uses TanStack Query and PokéAPI as an example, but any data source works.

Final structure

1. Install the primitive

2. The list

Start with data and a renderItem. PokedexItems already renders a responsive grid.

Tip

renderItem already applies the key via getItemKey (defaults to item.name), so you can omit key inside the render.

Add PokedexSearch. Without any extra configuration, the root filters items by comparing the normalized query against item.name.

4. Pagination

pageSize activates an incremental window and PokedexLoadMore advances it. The window resets automatically whenever the query or filters change.

Tip

Prefer infinite scroll? usePokedex() exposes loadMore and hasMore — connect them to an IntersectionObserver and remove the button.

5. Empty state

PokedexEmpty appears only when there are no matches. PokedexClear resets the search and disables itself automatically.

6. Controlled state

Up to this point the root was managing its own state. To sync with the URL (or a form, or global state), pass it as controlled: query + onQueryChange.

7. Domain filters

PokedexFilterState covers generations, primary/secondary type, legendaries, and mythicals. Only your app knows which generation each Pokémon belongs to, so there are two strategies:

A. Pre-filter in your data layer and disable the internal matcher:

B. Inject a filterFn with access to your indices:

The filter menu can be any UI (dropdown, sheet, sidebar) that reads toggles from context:

Warning

If your filters depend on remote data (e.g., which Pokémon are Water-type), pass loading to the root while they resolve — this prevents PokedexEmpty from flashing a false "no results" state.

Result

The Pokédex on this site (/pokedex) is built exactly this way: controlled state in the page, data via TanStack Query + PokéAPI, filters pre-calculated with shouldFilter={false}, and a custom filter menu over dropdown-menu. It is the reference implementation for this guide.