or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-react.mdcss-styling.mddom-integration.mderror-boundaries.mdevent-system.mdindex.mdreact-hooks.mdrouting.mdserver-side-rendering.mdtesting-utilities.md
tile.json

routing.mddocs/

Client-Side Routing

Built-in client-side routing solution with URL watching, navigation functions, and React hooks integration.

Types

type url = {
  path: list(string),  /* ["book", "title", "edit"] for "/book/title/edit" */
  hash: string,        /* "section1" for "#section1" (# stripped) */
  search: string,      /* "q=test" for "?q=test" (? stripped) */
};

type watcherID;

Navigation Functions

/* Update URL by pushing new history entry */
let push: string => unit;

/* Update URL by replacing current history entry */
let replace: string => unit;

URL Watching

/* Start watching URL changes, returns subscription token */
let watchUrl: (url => unit) => watcherID;

/* Stop watching URL changes */
let unwatchUrl: watcherID => unit;

/* Get current URL (marked dangerous - use with caution) */
let dangerouslyGetInitialUrl: (~serverUrlString: string=?, unit) => url;

React Hook

/* Hook for watching URL changes with SSR support */
let useUrl: (~serverUrl: url=?, unit) => url;

Usage Examples

Basic Routing

[@react.component]
let make = () => {
  let url = ReasonReactRouter.useUrl();
  
  switch (url.path) {
  | [] => <HomePage />
  | ["about"] => <AboutPage />
  | ["products"] => <ProductsPage />
  | ["products", productId] => <ProductPage productId />
  | ["user", userId, "profile"] => <UserProfile userId />
  | _ => <NotFoundPage />
  }
}

Navigation with State

[@react.component]
let make = () => {
  let url = ReasonReactRouter.useUrl();
  let (selectedTab, setSelectedTab) = React.useState(() => "overview");
  
  let navigateToTab = (tab) => {
    setSelectedTab(_ => tab);
    ReasonReactRouter.push("/dashboard/" ++ tab);
  };
  
  <div>
    <nav>
      <button onClick={_ => navigateToTab("overview")}>
        {React.string("Overview")}
      </button>
      <button onClick={_ => navigateToTab("settings")}>
        {React.string("Settings")}
      </button>
    </nav>
    
    {switch (url.path) {
     | ["dashboard", "overview"] => <DashboardOverview />
     | ["dashboard", "settings"] => <DashboardSettings />
     | _ => <DashboardOverview />
     }}
  </div>
}

URL Parameters and Query Handling

let parseQuery = (search: string): list((string, string)) => {
  search
  |> Js.String.split("&")
  |> Array.to_list
  |> List.map(pair => {
       let parts = Js.String.split("=", pair);
       switch (parts) {
       | [|key, value|] => (key, value)
       | [|key|] => (key, "")
       | _ => ("", "")
       }
     })
};

[@react.component]
let make = () => {
  let url = ReasonReactRouter.useUrl();
  let queryParams = parseQuery(url.search);
  
  let getParam = (key: string) => {
    queryParams
    |> List.find_opt(((k, _)) => k === key)
    |> Option.map(((_, v)) => v)
  };
  
  let currentPage = getParam("page")->Option.getWithDefault("1");
  let searchQuery = getParam("q")->Option.getWithDefault("");
  
  <div>
    <SearchResults query=searchQuery page=currentPage />
    <Pagination 
      currentPage={int_of_string(currentPage)}
      onPageChange={(page) => {
        let newQuery = "?page=" ++ string_of_int(page) ++ "&q=" ++ searchQuery;
        ReasonReactRouter.push("/search" ++ newQuery);
      }}
    />
  </div>
}

Programmatic Navigation

[@react.component] 
let make = () => {
  let handleLogin = (userId) => {
    /* Redirect after login */
    ReasonReactRouter.push("/dashboard");
  };
  
  let handleLogout = () => {
    /* Replace history to prevent back navigation to protected route */
    ReasonReactRouter.replace("/login");
  };
  
  let goBack = () => {
    /* Custom back navigation */
    [%raw "window.history.back()"];
  };
  
  <div>
    <button onClick={_ => handleLogin("123")}>
      {React.string("Login")}
    </button>
    <button onClick={_ => handleLogout()}>
      {React.string("Logout")} 
    </button>
    <button onClick={_ => goBack()}>
      {React.string("Go Back")}
    </button>
  </div>
}

Server-Side Rendering

/* For SSR, provide initial URL */
[@react.component]
let make = (~serverUrl: option(ReasonReactRouter.url)=?, ()) => {
  let url = ReasonReactRouter.useUrl(~serverUrl?, ());
  
  switch (url.path) {
  | ["ssr-page"] => <SSRPage />
  | _ => <HomePage />
  }
}

/* On server */
let renderApp = (requestUrl: string) => {
  let serverUrl = ReasonReactRouter.dangerouslyGetInitialUrl(~serverUrlString=requestUrl, ());
  ReactDOMServer.renderToString(<App serverUrl />);
};