Skip to content
Michi
Esc
navigateopen⌘Jpreview
On this page

Getting Started

Install Michi and build your first route.

Installation

Michi is a monorepo package. Clone the repo and install dependencies:

git clone https://github.com/atharvdange618/Michi.git
cd Michi
pnpm install

The router package lives at packages/michi. The demo app lives at apps/demo.

Setting up the Router

Define your route tree and wrap your app in RouterProvider:

import { Router, RouterProvider } from "michi";
import RootLayout from "./routes/__root";
import HomePage from "./routes/index";
import AboutPage from "./routes/about";
import UserPage from "./routes/user/$id";

const router = new Router([
  {
    path: "__root",
    component: RootLayout,
    children: [
      { path: "/", component: HomePage },
      { path: "/about", component: AboutPage },
      { path: "/user/$id", component: UserPage },
    ],
  },
]);

function App() {
  return <RouterProvider router={router} />;
}

Writing a Route Component

A route component is a regular React component. Use useParams to read dynamic segments:

import { useParams } from "michi";

export default function UserPage() {
  const { id } = useParams<{ id: string }>();
  return <h1>Hello, {id}</h1>;
}

Adding a Loader

Loaders fetch data before the component renders. Export a loader function and pass it to the route definition:

import { useParams, useLoaderData } from "michi";
import type { LoaderContext } from "michi";

export async function loader({ params }: LoaderContext<{ id: string }>) {
  const res = await fetch(`/api/user/${params.id}`);
  return res.json();
}

export default function UserPage() {
  const user = useLoaderData<{ name: string; email: string }>();
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Wire it into the route definition:

import UserPage, { loader as userLoader } from "./routes/user/$id";

{ path: "/user/$id", component: UserPage, loader: userLoader }

Programmatic Navigation

Use the useRouter hook to navigate from event handlers or effects:

import { useRouter } from "michi";

function LogoutButton() {
  const router = useRouter();

  return <button onClick={() => router.navigate("/")}>Log out</button>;
}

Use Link for client-side navigation. It renders an <a> tag with href for accessibility:

import { Link } from "michi";

<Link to="/about">About</Link>;

Error Handling

Each route can define a custom error component:

export function errorComponent({ error }: { error: unknown }) {
  return <div>Something went wrong: {String(error)}</div>;
}

Or handle errors at the loader level – errors thrown in loaders are caught and rendered by the route’s error boundary without crashing the rest of the app.

Next Steps

Last updated on July 19, 2026

Was this page helpful?