Built-in client-side routing solution with URL watching, navigation functions, and React hooks integration.
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;/* Update URL by pushing new history entry */
let push: string => unit;
/* Update URL by replacing current history entry */
let replace: string => unit;/* 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;/* Hook for watching URL changes with SSR support */
let useUrl: (~serverUrl: url=?, unit) => url;[@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 />
}
}[@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>
}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>
}[@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>
}/* 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 />);
};