Cosmic Developer handles navigation entirely on the client using React Router v6. There is no server involved in resolving page URLs — every route change is managed in the browser by React Router’sDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/cosmic-developer/llms.txt
Use this file to discover all available pages before exploring further.
<Routes> and <Route> components, wrapped inside a single <AnimatePresence> that drives cross-page transition animations.
Why Hash Routing?
The app usesHashRouter rather than BrowserRouter deliberately. When a React SPA is deployed to a static host (GitHub Pages, Netlify, S3, etc.), the server has no knowledge of client-side routes. If a user refreshes on /projects, the server looks for a file at that path, finds nothing, and returns a 404.
HashRouter avoids this problem entirely by storing the current route in the URL fragment (#). The browser never sends the fragment to the server — the full URL seen by the user might be https://example.com/#/projects, but the server only sees a request for /. React Router then reads the fragment and renders the correct page component.
Static HTML stubs in the pages/ directory (pages/About.html, pages/Projects.html, etc.) exist as pre-rendered fallback shells, but the interactive SPA experience is always delivered by the JavaScript bundle.
Route Table
Every route is registered inAppRoutes inside assets/main.js. The nav labels come from the navItems array defined in Navigation.js, where each item has a path, a space-themed label used in the overlay menu, and a plain desc used as the accessible title:
| Path | Page Component | Nav Label |
|---|---|---|
/ | Home | Earthrise |
/about | About | Origin Coordinates |
/projects | Projects | Probes & Payloads |
/skills | Skills | Instruments |
/work | Work | Mission Log |
/case-studies | CaseStudies | Flight Recordings |
/articles | Articles | Transmissions |
/testimonials | Testimonials | Ground Control Says |
/contact | Contact | Open Channel |
AppRoutes Implementation
AppRoutes reads the current location from useLocation() and passes it as both the <AnimatePresence> key and the <Routes> location prop. This is the pattern required to make exit animations fire correctly — if location were not passed explicitly, React Router would unmount the old page before Framer Motion can animate it out.
AnimatePresence and Page Transitions
AnimatePresence with mode="wait" ensures that the outgoing page fully completes its exit animation before the incoming page begins its entrance. Without mode="wait", both animations would run simultaneously and overlap visually.
Each page component is wrapped in <PageTransition>, which is a motion.div that defines the three animation states:
key={location.pathname} prop on <Routes> is what triggers AnimatePresence to treat each navigation as a mount/unmount cycle. When the pathname changes, React sees a new key, unmounts the current <Routes> subtree (running exit animations), then mounts the new one (running entrance animations).
Adding a New Route
Create the page component
Add a new function component in
assets/main.js (or import it from a separate file). Wrap the return value in <PageTransition> to get the standard enter/exit animation:Add the route to navItems in Navigation.js
The
navItems array in Navigation.js drives both the full-screen overlay menu and the active-link highlighting. Add an entry with a path, a thematic label, and a plain desc: