Skip to content

Routing

flexium/router is a tiny client-side router. Define routes declaratively with <Routes> + <Route>, navigate with <Link>, read state with useRouter().

Basic setup

tsx
import { Routes, Route, Link } from 'flexium/router'

function Home()  { return <h1>Home</h1> }
function About() { return <h1>About</h1> }

function App() {
  return (
    <>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>

      <Routes>
        <Route path="/" component={Home} />
        <Route path="/about" component={About} />
      </Routes>
    </>
  )
}

Clicking <Link> calls history.pushState and updates the route signal. Browser back/forward work automatically.

Dynamic params

tsx
<Route path="/users/:id" component={UserPage} />

function UserPage() {
  const { params } = useRouter()
  return <h1>User #{params.id}</h1>
}

Multiple params:

tsx
<Route path="/posts/:category/:slug" component={Post} />
// /posts/tech/hello-world → params = { category: 'tech', slug: 'hello-world' }

Catch-all

tsx
<Route path="*" component={NotFound} />
// or
<Route path="/docs/*" component={Docs} />

Nested routes

Use <Outlet /> for layout patterns:

tsx
import { Routes, Route, Outlet, Link } from 'flexium/router'

function DashboardLayout() {
  return (
    <div class="dashboard">
      <aside>
        <Link to="/dashboard">Home</Link>
        <Link to="/dashboard/settings">Settings</Link>
      </aside>
      <main>
        <Outlet />
      </main>
    </div>
  )
}

<Routes>
  <Route path="/dashboard" component={DashboardLayout}>
    <Route path="/" component={DashboardHome} />
    <Route path="/settings" component={Settings} />
  </Route>
</Routes>

The layout component wraps every child route's content via <Outlet />.

Programmatic navigation

tsx
import { useRouter } from 'flexium/router'

function LoginForm() {
  const { navigate } = useRouter()

  async function submit() {
    await login(...)
    navigate('/dashboard')  // SPA-style, no full page reload
  }
}

navigate(path, options?):

  • options.replace: true — replaces history entry instead of pushing
  • options.state — passes state via History API

Reading route state

tsx
function Page() {
  const { location, params, navigate } = useRouter()

  // location.pathname  — current URL path
  // location.search    — query string
  // params             — captured route params (object)
  // navigate(path)     — programmatic navigation
}

Route guards

Restrict access via beforeEnter:

tsx
function isLoggedIn() { /* ... */ }

<Route
  path="/admin"
  component={AdminDashboard}
  beforeEnter={(params) => {
    if (!isLoggedIn()) {
      navigate('/login')
      return false  // skip rendering
    }
    return true
  }}
/>

Query strings

useRouter().location.search gives you the raw ?foo=bar string. Parse with URLSearchParams:

tsx
const { location } = useRouter()
const params = new URLSearchParams(location.search)
const page = Number(params.get('page') ?? 1)

<Link to="/x"> is the SPA-style navigation. Renders as <a href="/x"> but intercepts the click and calls navigate('/x').

Use plain <a href="..."> for external links or when you want a full page reload.

Performance

The router uses signal-based path matching — only components inside <Routes> re-render on navigation. Other parts of the tree (header, footer, sidebar) stay untouched.

Next

Stylingflexium/css atomic styles.

Released under the MIT License.