Skip to content
Michi
Esc
navigateopen⌘Jpreview
On this page

Introduction

A client-side router built from first principles.

Michi (道)

atharvdange618/MichiMichi is an educational router built from first principles to explore the architecture behind modern routing systems. Each feature is implemented as a self-contained slice that answers one fundamental question about how client-side routing works, from the History API to nested layouts, data loading, and beyond.10

I’ve used React Router and TanStack Router in production for years. They work great. But I never really understood how they work. So I started building one from scratch to find out.

Michi is a client-side router built from first principles – no routing libraries, no framework abstractions, just the raw History API and React primitives. It’s not trying to replace anything. It’s here to answer the question: what actually happens between the click and the render?

The name 道 (michi) is Japanese for “path” or “the way.” Seemed fitting.

The idea

Every feature is built as a slice – a self-contained, demonstrable milestone that builds on the previous one. Each slice answers a specific question about how routing works:

Slice Status What it covers
1 Done History API - pushState, popstate, and the core router loop
2 Done Route Matching - turning URL patterns into regex that actually matches
3 Done Nested Routes - the route tree, <Outlet />, and layouts that persist
4 Done Data Loaders - render-as-you-fetch with parallel execution
5 Done Error Boundaries - isolating failures per route
6 Done Prefetch on Hover - running loaders early
7 Planned Search Params - typed, serializable URL state
8 Planned File-Based Routing - codegen from filesystem
9 Planned Typed Routes - compile-time path and param safety

The stack

  • Monorepo: Turborepo + pnpm workspaces
  • Language: TypeScript (strict)
  • Demo app: Vite + React 19
  • Router package: Pure TypeScript, zero runtime dependencies
  • Testing: Vitest + @testing-library/react

What’s done

Slices 1 through 6 are built and working. The router supports:

  • Dynamic params/user/$id matches /user/atharv and extracts { id: "atharv" }
  • Wildcard routes/files/* matches /files/public/uploads/report.pdf
  • Nested layouts/settings wraps its children in a sidebar layout via <Outlet />
  • Pathless layouts_auth wraps routes without adding a URL segment
  • Nested outlets – 3+ levels of rendering depth (root, layout, page)
  • Programmatic navigationuseRouter() hook for navigating from event handlers
  • Persistent layouts – nav and sidebars stay mounted across navigations
  • Data loaders – fetch data before rendering with useLoaderData()
  • Parallel loader execution – all matched route loaders run simultaneously
  • Race condition protection – stale loader results are discarded on rapid navigation
  • Per-route error boundaries – loader and render errors are isolated to the failing route
  • Custom error components – each route can define its own errorComponent
  • Prefetch on hover – run loaders early when the user hovers a link

Explore the docs

Last updated on July 19, 2026

Was this page helpful?