Skip to content

Instantly share code, notes, and snippets.

@hnordt
Last active September 17, 2025 00:33
Show Gist options
  • Select an option

  • Save hnordt/bfe8f6893f0ec841530d6f6dfa79b155 to your computer and use it in GitHub Desktop.

Select an option

Save hnordt/bfe8f6893f0ec841530d6f6dfa79b155 to your computer and use it in GitHub Desktop.
import { useReducer, useEffect } from "react";
// pathToRegexp can be replaced with URLPattern once browser support improves
// https://developer.mozilla.org/en-US/docs/Web/API/URLPattern
import pathToRegexp from "path-to-regexp";
const Router = {
PushStateEventType: "pushstate",
ReplaceStateEventType: "replacestate",
PopStateEventType: "popstate",
HashChangeEventType: "hashchange",
get location() {
return {
href: window.location.href,
pathname: window.location.pathname,
searchParams: new URLSearchParams(window.location.search),
hash: window.location.hash.replace(/^#/, ""),
};
},
match(pattern: string) {
const result = pathToRegexp.match(pattern)(this.location.pathname);
if (!result) {
return null;
}
return {
pathname: result.path,
params: new Map(Object.entries(result.params)),
};
},
subscribe(callback: () => void) {
window.addEventListener(this.PushStateEventType, callback);
window.addEventListener(this.ReplaceStateEventType, callback);
window.addEventListener(this.PopStateEventType, callback);
window.addEventListener(this.HashChangeEventType, callback);
return () => {
window.removeEventListener(this.PushStateEventType, callback);
window.removeEventListener(this.ReplaceStateEventType, callback);
window.removeEventListener(this.PopStateEventType, callback);
window.removeEventListener(this.HashChangeEventType, callback);
};
},
push(url: string | URL) {
history.pushState(null, "", url);
dispatchEvent(new CustomEvent(this.PushStateEventType));
},
replace(url: string | URL) {
history.replaceState(null, "", url);
dispatchEvent(new CustomEvent(this.ReplaceStateEventType));
},
go(delta: number) {
history.go(delta);
},
};
function useRouter() {
const [, forceUpdate] = useReducer((x) => x + 1, 0);
useEffect(() => Router.subscribe(forceUpdate), []);
return Router;
}
function SignInScreen() {
const router = useRouter();
if (!router.match("/sign-in")) {
return null;
}
return (
<>
<h1>Sign In</h1>
...
</>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment