@zessjs/router
v1.1.6
Published
Zess router π Client-side navigation, dynamic & nested routing for web apps
Readme
Zess router π Client-side navigation, dynamic & nested routing for web apps.
β¨ Features
- π― Simple and Lightweight: Easy-to-use API with minimal configuration required
- β‘ Performance Optimized: Caches each route component for excellent rendering performance
- π Multiple Modes: Supports both hash mode and history mode for different deployment environments
- π§© Nested Routes: Create complex route hierarchies with nested components
- π Dynamic Navigation: Programmatically navigate between routes using hooks
- π Search Params Handling: Built-in support for managing query parameters
- π Type Safety: Complete TypeScript type definitions
- π Seamless Integration: Works perfectly with @zessjs/core reactive system
π¦ Installation
# Using npm
npm install @zessjs/router
# Using yarn
yarn add @zessjs/router
# Using pnpm
pnpm add @zessjs/routerπ Basic Usage
Setting Up the Router
import { render } from '@zessjs/core'
import { Router, Route } from '@zessjs/router'
function RootLayout(props) {
return <div>{props.children}</div>
}
function HomePage() {
return <h1>Welcome to Home Page</h1>
}
function AboutPage() {
return <h1>About Us</h1>
}
render(
() => (
<Router root={RootLayout}>
<Route path="/" component={HomePage} />
<Route path="/about" component={AboutPage} />
</Router>
),
document.getElementById('app'),
)Using Navigation Links
import { Link } from '@zessjs/router'
function Navigation() {
return (
<nav>
<Link to="/" class="nav-link">
Home
</Link>
<Link to="/about" class="nav-link">
About
</Link>
</nav>
)
}Programmatically Navigating
import { useNavigate } from '@zessjs/router'
function NavigationButtons() {
const navigate = useNavigate()
return (
<div>
<button onClick={() => navigate('/')}>Go to Home</button>
<button onClick={() => navigate('/about', { replace: true })}>
Go to About
</button>
</div>
)
}π§ Advanced Usage
Nested Routes
import { Router, Route } from '@zessjs/router'
function AppLayout(props) {
return (
<div class="app">
<header>Zess Application</header>
<main>{props.children}</main>
</div>
)
}
function UserLayout(props) {
return (
<div class="user-section">
<nav class="user-nav">User Navigation</nav>
<div class="user-content">{props.children}</div>
</div>
)
}
function HomePage() {
return <h1>Welcome to Home Page</h1>
}
function UserList() {
return <h2>User List</h2>
}
function UserProfile() {
return <h2>User Profile</h2>
}
render(
() => (
<Router root={AppLayout}>
<Route path="/" component={HomePage} />
{/* Nested routes */}
<Route path="/users" component={UserLayout}>
<Route path="/" component={UserList} />
<Route path="/profile" component={UserProfile} />
</Route>
</Router>
),
document.getElementById('app'),
)Working with Search Parameters
import { useSearchParams } from '@zessjs/router'
function SearchComponent() {
const [searchParams, setSearchParams] = useSearchParams()
const handleSearch = (event) => {
event.preventDefault()
const formData = new FormData(event.target)
const query = formData.get('query')
setSearchParams({ query })
}
return (
<div>
<form onSubmit={handleSearch}>
<input type="text" name="query" defaultValue={searchParams.query} />
<button type="submit">Search</button>
</form>
{searchParams.query && <p>Searching for: {searchParams.query}</p>}
</div>
)
}Using Different Router Modes
// Hash mode (default)
render(
() => (
<Router mode="hash" root={RootLayout}>
<Route path="/" component={HomePage} />
</Router>
),
document.getElementById('app'),
)
// History mode
render(
() => (
<Router mode="history" root={RootLayout}>
<Route path="/" component={HomePage} />
</Router>
),
document.getElementById('app'),
)π API Reference
Components
Router(props: RouterProps): JSX.Element
The main router component that wraps all your routes and provides the routing context.
Parameters:
mode: Optional routing mode, either'hash'or'history'. Defaults to'hash'root: Optional component that wraps the rendered component for all routeschildren:<Route>components to be rendered
Example:
<Router mode="hash" root={RootLayout}>
<Route path="/" component={HomePage} />
<Route path="/about" component={AboutPage} />
</Router>Route(props: RouteProps): JSX.Element
Defines a route and its corresponding component.
Parameters:
path: The path pattern for this routesensitive: Optional flag to make the path matching case-sensitive. Defaults tofalsecomponent: Optional component to render when the route is matchedchildren: Optional nested<Route>components
Example:
// Basic route
<Route path="/about" component={AboutPage} />
// Case-sensitive route
<Route path="/API" sensitive component={ApiPage} />
// Route with nested routes
<Route path="/dashboard" component={DashboardLayout}>
<Route path="/stats" component={StatsPage} />
<Route path="/settings" component={SettingsPage} />
</Route>
// Wildcard route (matches any path)
<Route path="*" component={NotFoundPage} />Link(props: LinkProps): JSX.Element
Creates a navigable link to another route.
Parameters:
to: The destination path, can include query stringsrelative: Optional flag to navigate to the relative path. Defaults totruereplace: Optional flag to replace the current history entry instead of pushing a new onenoScroll: Optional flag to prevent scrolling to top when navigatingstate: Optional state object to pass tohistory.state, defaults tonullstyle: Optional CSS stylesclass: Optional CSS class nameactiveClass: Optional CSS class name to apply when the link is activeend: Optional flag to match the path exactly. When set totrue, the link will only be active if the current path matches exactlychildren: Optional content for the link
Example:
// Basic link
<Link to="/home">Home</Link>
// Link with absolute path
<Link to="/about" relative={false}>About (Exact)</Link>
// Link with replace
<Link to="/login" replace>Login</Link>
// Link with noScroll
<Link to="/details" noScroll>View Details (No Scroll)</Link>
// Link with styles
<Link to="/contact" style={{ color: 'blue' }}>Contact</Link>
// Link with className
<Link to="/profile" class="user-link">Profile</Link>
// Link with activeClass
<Link to="/dashboard" activeClass="active-nav">Dashboard</Link>
// Link with end prop
<Link to="/settings" end>Settings (Exact Match)</Link>
// Link with query parameters
<Link to="/products?category=electronics&sort=price">Products (Electronics)</Link>
// Link with state
<Link to="/account" state={{ from: 'search', sourceId: 1 }}>
View Product Details
</Link>Primitives
useNavigate(): (href: string, options?: NavigateOptions) => void
Hook that returns a function to programmatically navigate between routes.
Returns: A function that accepts a path and optional navigation options
href: The destination path, can include query stringsoptions: Optional configurationrelative: Iffalse, navigates to the absolute path without relative base. Defaults totruereplace: Iftrue, replaces the current history entrynoScroll: Iftrue, prevents scrolling to top when navigatingstate: Optional state object to pass tohistory.state, defaults tonull
Example:
function NavigationComponent() {
const navigate = useNavigate()
return (
<>
<button onClick={() => navigate('/')}>Home</button>
<button onClick={() => navigate('/products', { relative: false })}>
Products
</button>
<button onClick={() => navigate('/login', { replace: true })}>
Login
</button>
<button onClick={() => navigate('/details', { noScroll: true })}>
View Details (No Scroll)
</button>
<button onClick={() => navigate('/search?q=zess&page=1')}>
Search Zess (Page 1)
</button>
<button onClick={() => navigate('/checkout', { state: { userId: 1 } })}>
Proceed to Checkout
</button>
</>
)
}useSearchParams(): [SearchParams, (params: Record<string, any>, options?: SearchParamsOptions) => void]
Hook that provides access to search parameters and a function to update them.
Returns: An array containing
searchParams: A reactive object with current search parameters that auto-updates whenlocation.searchchanges or when modified viasetSearchParamssetSearchParams: A function to update search parametersparams: Search parameters to merge with existing ones. Setting a property value toundefined,nullor an empty string removes that propertyoptions: Optional configuration objectreplace: Optional flag to replace the current history entry instead of pushing a new onestate: Optional state object to pass tohistory.state, defaults tonull
Example:
function ProductFilter() {
const [searchParams, setSearchParams] = useSearchParams()
const setCategory = (category) => setSearchParams({ category })
const search = (keyword, searchType) => {
setSearchParams(
{ keyword },
{
replace: true,
state: { searchType },
},
)
}
const clearFilters = () => {
setSearchParams({ category: undefined, keyword: undefined })
}
return (
<div>
<p>Current category: {searchParams.category || 'All'}</p>
<p>Search keyword: {searchParams.keyword || 'None'}</p>
<button onClick={() => setCategory('shoes')}>Set Category</button>
<button onClick={() => search('sale', 'quick')}>Search with Type</button>
<button onClick={clearFilters}>Clear Filters</button>
</div>
)
}useBeforeLeave(listener: RouteGuardListener): void
Hook that registers a listener to be called before leaving the current route. This allows you to intercept navigation attempts and potentially prevent them, for example, to warn users about unsaved changes.
Parameters:
listener: A function that will be called with aRouteGuardEventobject when navigation away from the current route is attemptedevent: The route guard event object containing:to: The destination path being navigated tofrom: The current path being navigated fromoptions: Navigation options includingrelative,replace, andnoScrolldefaultPrevented: Boolean indicating if the navigation has been preventedpreventDefault: Function to call to prevent the navigationretry: Function to retry the navigation later, with an optionalforceparameter to bypass guards
Example:
function FormEditor() {
const [hasUnsavedChanges, setHasUnsavedChanges] = useSignal(false)
useBeforeLeave((event) => {
if (hasUnsavedChanges()) {
const confirmed = window.confirm(
'You have unsaved changes. Are you sure you want to leave?',
)
if (!confirmed) {
event.preventDefault()
}
}
})
return (
<div>
<input type="text" onChange={() => setHasUnsavedChanges(true)} />
<button onClick={() => setHasUnsavedChanges(false)}>Save</button>
</div>
)
}useLocation(): Location
Hook that returns a reactive Location object containing information about the current URL. This object automatically updates when the URL changes.
Returns: A reactive Location object with pathname and other properties
pathname: The path portion of the URL excluding the query stringsearch: The query string portion of the URLhash: The hash portion of the URL including the#symbolstate: The state object associated with the current history entry passed viauseNavigateor<Link>componentquery: A reactive object containing all query parameters of the URL
Example:
function LocationDisplay() {
const location = useLocation()
return (
<div>
<h2>Current Location Information</h2>
<p>pathname: {location.pathname}</p>
<p>search: {location.search}</p>
<p>hash: {location.hash}</p>
<p>state: {JSON.stringify(location.state)}</p>
<p>query: {JSON.stringify(location.query)}</p>
</div>
)
}π Compatibility
The Zess router is compatible with:
- Node.js >=18.12.0
- Modern browsers (Chrome, Firefox, Safari, Edge)
