olova-router
v1.0.21
Published
Next.js-inspired router for Olova framework with 9 advanced routing features including prefetching, transitions, metadata, dynamic routes, route groups, parallel routes, and intercepting routes
Maintainers
Readme
olova-router
A powerful, feature-rich router for Olova framework with advanced routing capabilities, middleware support, and lazy loading.
Features
- 🚀 Browser History & Hash Modes - Support for both history and hash-based routing
- 🛡️ Route Guards - Protect routes with beforeEnter guards and middleware
- 🎯 Dynamic Routes - Support for dynamic params like
/blog/:slugand wildcards - 📦 Lazy Loading - Code splitting with lazy component loading
- 🔗 Named Routes - Reference routes by name instead of path
- 🎨 Active Link Styling - Automatic active state detection for links
- 📍 Breadcrumbs - Built-in breadcrumb generation utilities
- 🔄 Middleware Chain - Composable middleware for cross-cutting concerns
- 📜 Route History - Track navigation history with utilities
- ⚡ Type Safe - Full TypeScript support with comprehensive types
Installation
npm install olova-router
# or
pnpm add olova-router
# or
yarn add olova-routerQuick Start
<script>
import { BrowserRouter, Link } from "olova-router";
import Home from "./Home.olova";
import About from "./About.olova";
import Blog from "./Blog.olova";
const routes = [
{
path: "/",
component: Home,
name: "home"
},
{
path: "/about",
component: About,
name: "about"
},
{
path: "/blog/:slug",
component: Blog,
name: "blog.show"
}
]
</script>
<nav>
<Link href="/">Home</Link>
<Link name="about">About</Link>
<Link
name="blog.show"
params={{ slug: "getting-started" }}
activeClass="text-emerald-300"
>
Blog
</Link>
</nav>
<BrowserRouter routes={routes} />Core API
Router Components
Router- Base router componentBrowserRouter- History mode router (recommended)HashRouter- Hash mode router (for static hosting)Link- Navigation link componentOutlet- Nested route outlet
Router Hooks
import { useRouter, useRoute, useParams, useQuery } from "olova-router";
// Get full router API
const router = useRouter();
// Get current route location
const route = useRoute();
// Get route parameters
const params = useParams();
// Get query parameters
const query = useQuery();Navigation
import { navigate, link } from "olova-router";
// Programmatic navigation
navigate("/about");
navigate({ name: "blog.show", params: { slug: "hello" } });
// With options
navigate("/home", { replace: true, scroll: true });
// Link handler
const handleClick = link("/about");Advanced Features
Route Guards
Protect routes with guards and middleware:
import { createConditionalGuard, createParamGuard } from "olova-router/guards";
const routes = [
{
path: "/admin",
component: AdminPanel,
beforeEnter: createConditionalGuard(
() => isUserAdmin(),
"/unauthorized"
)
},
{
path: "/user/:id",
component: UserProfile,
beforeEnter: createParamGuard(
"id",
(id) => /^\d+$/.test(id),
"/not-found"
)
}
];Middleware Chain
Compose multiple middleware for route transitions:
import {
createMiddlewareChain,
createAuthMiddleware,
createAnalyticsMiddleware,
createScrollMiddleware
} from "olova-router/middleware";
const middleware = createMiddlewareChain()
.use(createAuthMiddleware(() => isAuthenticated(), "/login"))
.use(createAnalyticsMiddleware((path) => trackPageView(path)))
.use(createScrollMiddleware(true));
// Execute middleware
const result = await middleware.execute(context);Lazy Loading
Code split components with lazy loading:
import { lazyComponent, lazyComponentWithFallback } from "olova-router/lazy";
const routes = [
{
path: "/dashboard",
component: lazyComponent(() => import("./Dashboard.olova"))
},
{
path: "/settings",
component: lazyComponentWithFallback(
() => import("./Settings.olova"),
LoadingSpinner,
ErrorFallback
)
}
];Breadcrumbs
Generate breadcrumbs from route location:
import { generateBreadcrumbs, generateBreadcrumbsFromMatches } from "olova-router/breadcrumbs";
const route = useRoute();
const breadcrumbs = generateBreadcrumbs(route, {
home: "Home",
blog: "Blog",
settings: "Settings"
});
// Or from route matches
const breadcrumbs = generateBreadcrumbsFromMatches(route.matches);Route History
Track navigation history:
import { createRouterHistory } from "olova-router/history";
const history = createRouterHistory(50); // max 50 entries
history.push("/home");
history.push("/about");
const prev = history.back(); // { path: "/home", timestamp: ... }
const next = history.forward(); // { path: "/about", timestamp: ... }
const entries = history.getHistory();Route Configuration
Basic Route
{
path: "/about",
component: About,
name: "about"
}Dynamic Route
{
path: "/blog/:slug",
component: BlogPost,
name: "blog.post"
}Wildcard Route
{
path: "/docs/*",
component: DocsLayout,
name: "docs"
}Nested Routes
{
path: "/dashboard",
component: DashboardLayout,
children: [
{ index: true, component: DashboardHome },
{ path: "settings", component: DashboardSettings },
{ path: "profile", component: DashboardProfile }
]
}Route with Guards
{
path: "/admin",
component: AdminPanel,
beforeEnter: async (context) => {
if (!isAdmin()) {
return "/unauthorized";
}
return true;
}
}Route with Props
{
path: "/user/:id",
component: UserProfile,
props: (context) => ({
userId: context.params.id,
isAdmin: context.query.admin === "true"
})
}Route Redirect
{
path: "/old-path",
redirect: "/new-path"
}Link Component
<script>
import { Link } from "olova-router";
</script>
<!-- Simple href -->
<Link href="/about">About</Link>
<!-- Named route -->
<Link name="blog.post" params={{ slug: "hello" }}>Read Post</Link>
<!-- With query and hash -->
<Link
name="search"
query={{ q: "olova" }}
hash="results"
>
Search
</Link>
<!-- Active styling -->
<Link
href="/about"
class="nav-link"
activeClass="active"
inactiveClass="inactive"
exact
>
About
</Link>
<!-- Custom attributes -->
<Link
href="/external"
target="_blank"
rel="noopener noreferrer"
title="External Link"
>
External
</Link>Router Props
type RouterProps = {
routes: RouterRoutes;
mode?: "auto" | "history" | "hash"; // default: "auto"
base?: string; // default: "/"
scroll?: boolean; // default: true
};TypeScript Support
Full type safety with comprehensive types:
import type {
RouteLocation,
RouteParams,
RouteQuery,
RouteTarget,
RouterApi,
RouteGuardContext
} from "olova-router";Vite Plugin
Auto-generate routes from file structure:
// vite.config.ts
import { olovaRouter } from "olova-router/vite";
export default {
plugins: [olovaRouter()]
};Best Practices
- Use Named Routes - More maintainable than hardcoded paths
- Lazy Load Heavy Components - Improve initial load time
- Protect Sensitive Routes - Use guards for auth/permissions
- Handle 404s - Always include a wildcard route
- Scroll Management - Enable scroll reset on navigation
- Type Your Routes - Leverage TypeScript for safety
Examples
Authentication Guard
import { createConditionalGuard } from "olova-router/guards";
const authGuard = createConditionalGuard(
async (context) => {
const token = localStorage.getItem("auth_token");
if (!token) return false;
const valid = await validateToken(token);
return valid;
},
"/login"
);
const routes = [
{
path: "/dashboard",
component: Dashboard,
beforeEnter: authGuard
}
];Analytics Tracking
import { createAnalyticsMiddleware } from "olova-router/middleware";
const analyticsMiddleware = createAnalyticsMiddleware((path) => {
gtag.pageview({
page_path: path,
page_title: document.title
});
});Composite Guards
import { createCompositeGuard } from "olova-router/guards";
const protectedGuard = createCompositeGuard([
createAuthMiddleware(() => isAuthenticated()),
createRoleMiddleware(() => userRole(), ["admin", "moderator"])
]);License
MIT
