Wat is een router?

Een router, in een front-end context heeft als taak een URL te koppelen aan een component.

React is gebaseerd op componenten, componenten zijn het kleinste bouwblok die react aanbiedt. Wanneer je een hele applicatie / website wilt maken (en niet slechts een klein deel van je pagina react is) dan wil je al snel URLs koppelen aan bepaalde component.

Je wilt bijvoorbeeld een <About /> component koppelen aan de URL /over-mij, of <PokemonList /> aan /pokemons etc etc.

Waarom is de URL zo belangrijk?

Een veelgemaakte fout in Single Page Applications (SPA) is dat de URL altijd hetzelfde is, welke pagina / view je ook aan het bekijken bent.Op deze manier voelt je react applicatie niet web native, in andere woorden jouw applicatie gedraagt zich dan niet als "website".

Gebruikers verwachten nou eenmaal dat een website / webapp zich op een bepaalde manier gedraagt. Nergens komt dit meer naar de voorgrond dan in hoe een adresbalk zou moeten werken.

Als de gebruiker naar iets navigeert dan wil je dat de URL zich aanpast, de drie redenen waarom dit zo belangrijk zijn:

  • Een gebruiker wil een URL kunnen delen! Het is heel irritant als jij een collega iets wilt laten zien, en je kan die persoon geen directe link sturen.

    Stel je voor dat je een artikel op nos.nl / nu.nl ziet en je deze delen, en je moet nog even nabellen via telefoon om te zeggen waar ze op het scherm moeten klikken. Je lacht er misschien om maar je ziet het helaas te vaak bij SPAs.

    Ik wil gewoon een URL kunnen sturen via mail / direct message en dan wil ik dat de andere persoon ziet wat ik zie (mits de persoon daar ook de rechten toe heeft).

  • Op het moment dat de URL de staat van de pagina / components reflecteert kan je met de terug knop van de browser navigeren.

  • Een URL kan dan ook eenvoudig worden gebookmarked.

Wat is file based routing?

File based routing, ofwel bestand gebaseerd, houdt in dat het file system bepaald welke pagina's er bestaan in je webapp / website.

Het volgende voorbeeld komt uit next.js, en is de basis van de website waar je nu naar kijkt:

Folder structuur met een 'app' folder met daarin verschillende subfolders zoals bijvoorbeeld 'wat-is-een-router' en 'wat-is-vite' in elk van deze subfolders staat een bestand genaamd 'page.tsx'

In next.js heeft dus een file based router die als volgt werkt: je maakt een folder genaamd app aan wanneer next.js binnen app dan bestanden genaamd page.tsx vindt dan maakt hij een pagina aan.

Binnen een page.tsx verwacht next.js een default export van een React component, dit is dan wat hij op de pagina toont. Zie als voorbeeld:

export default function WatIsEenRouterPage() {
  return (
    <Page title="Wat is een router?">
      <Section>
        <H1>Wat is een router?</H1>
      </Section
    </Page>
  )
}

Vraag is nu hoe je met dynamische routes omgaat? Je wilt bijvoorbeeld niet in een pokedex applicatie voor elke pokemon een pagina aan hoeven maken. In @tanstack/router lossen ze dit op door een $ teken voor de naam van de folder te zetten:

Folder structuur met een 'routes' folder met daarin verschillende subfolders zoals bijvoorbeeld 'pokedex' en en 'todo' in elk van deze subfolders staat een bestand genaamd 'index.tsx' wat de pagina's voorsteld. We zien ook binnen 'pokedex' een folder staan '$pokemonId' het dollar teken duidt dan aan dat dit een dynamische route is.

Wanneer je dan naar /pokedex/1, /pokedex/150 of /pokedex/noot navigeert, dan rendered @tanstack/router de index.tsx die direct onder de $pokemonId folder valt.

In next.js gebruiken zo overigens niet een $ maar blokhaken bijvoorbeeld [pokemonId].

Wat is component based routing?

Bij component based routing gebruik je react componenten om de structuur van de pagina's te definiëren. Neem dit voorbeeld van de wouter library:

export function PokedexApp() {
  return (
    <Switch>
      <Route path="create">
        <PokemonCreate />
      </Route>

      <Route path=":id/edit">
        <PokemonEdit />
      </Route>

      <Route path=":id/evolutions">
        <PokemonEvolutionForm />
      </Route>

      <Route path=":id">
        <PokemonDetail />
      </Route>

      <Route path="">
        <PokemonList />
      </Route>
    </Switch>
  );
}

Omdat ergens hogergelegen de PokedexApp in een /pokedex route staat:

<Route path="pokedex" nest>
  <PokedexApp />
</Route>

beginnen alle routes van de PokedexApp met /pokedex.

Ook in dit type routers zijn dynamische routes mogelijk. In wouter beginnen deze routes met een :id. Wanneer je dan naar /pokedex/1, /pokedex/150 of /pokedex/noot navigeert, dan rendered het PokemonDetail component.

Wat is code based routing?

In code based routing maak je je routes op basis van... nou ja code.

In @tanstack/router (code based routing mode) zijn dit bijvoorbeeld function calls:

import 
  { createRootRoute, createRoute } 
from '@tanstack/react-router'

const rootRoute = createRootRoute()

const indexRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/',
  component: HomePage,
})

const postEditorRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'pokedex/$pokemonId',
  comonent: PokemonDetail
})

In react-router (framework mode) zijn dit dan weer exports van een bepaald type.

import { index, route } from "@react-router/dev/routes";

export default [
  index("./home.tsx"),
  route("pokedex/:pokemonId", "./pokemon-detail.tsx"),
];

Natuurlijk zijn in code based routers ook dynamische routes mogelijk. In react-router beginnen dynamische onderdelen met een : zoals :pokemonId, in @tanstack/router met een $ zoals $pokemonId. Wanneer je dan naar /pokedex/1, /pokedex/150 of /pokedex/noot navigeert, dan rendered het de pokemon detail pagina.

File vs Component vs Code based

Over het algemeen bieden component en code based routers de mogelijkheid voor dynamisch geprogrammeerde routes. In andere woorden routes / pagina's die bijvoorbeeld vanuit een JSON kunnen worden gevoed.

Zo kan je een applicatie maken waarvan de back-end deels / helemaal kan dicteren welke pagina's er zijn, dit zou dan weer handig kunnen zijn bij hele dynamische applicaties.

File based routing excelleert dan weer in zijn eenvoud. Je plempt ergens een file neer en voila de pagina bestaat. Dit doet mij sterk denken aan hoe het vroeger werkte toen ik nog HTML websites met de hand schreef.

Wat is de beste router?

Het antwoord ligt heel erg aan de situatie. Gebruik je next.js / astro / @tanstack/start dan krijg je al een router cadeau.

Gebruik je vite dan kan je kiezen tussen react-router of wouter.

Qua features wint de ene router niet van de andere:

  • Alle routers hebben een manier om met dynamische path parameters te werken. Matchen op elke pokemon via /pokemon/:pokemonId bijvoorbeeld in wouter.

  • Alle routers bieden een abstractie voor het omgaan met search / query parameters om te gaan. Search parameters zijn de waardes in de URL aangeduid met ? en &. Bijvoorbeeld: /pokedex?query=bulb&page=1

  • Alle routers bieden manieren aan om sub routes / child routes de definiëren zodat je niet alle code op één plek hoeft te zetten.

Wat vindt Maarten?

Een foto van Maarten, een gemiddeld gezicht al zeg ik het zelf.

Gebruik gewoon een router punt! Niets is zo irritant als een SPA die zich niet gedraagt als web citizen.

Kies daarnaast de beste router die bij je project past. wouter is bijvoorbeeld erg fijn in combinatie met vite wanneer je een Single Page Application bouwt.

Contact

Neem contact op via e-mail, phone of app, als je vragen hebt of een offerte wilt aanvragen.

Vraag een offerte aan Boek kennismakingsgesprek