Now.js Framework Documentation
RouterManager
RouterManager
Now.js Router Manager - Manage routing and navigation in Single Page Applications (SPA)
Overview
RouterManager is a core component of Now.js that handles routing and navigation, supporting both hash-based routing (#/path) and history API (/path) with authentication guards, middleware, and route parameter extraction.
Use Cases:
- Single Page Application (SPA) routing
- Dynamic content loading without page refresh
- URL parameter and query string handling
- Route guards and authentication
- Nested routes and route groups
- Customizable 404 error handling
Browser Compatibility:
- Chrome, Firefox, Safari, Edge (modern versions)
- History API support for modern browsers
- Hash mode fallback for older browsers
Installation and Initialization
Loading the Script
RouterManager is included in Now.js core:
<script src="/Now/Now.js"></script>Or load separately:
<script src="/Now/js/RouterManager.js"></script>Basic Initialization
// Simple router initialization
await RouterManager.init({
enabled: true,
mode: 'history', // 'hash' or 'history'
base: '/'
});
// Register routes
RouterManager.register('/home', {
template: 'home',
title: 'Home Page'
});
RouterManager.register('/about', {
template: 'about',
title: 'About Us'
});Initialization via Now.js
await Now.init({
router: {
enabled: true,
mode: 'history',
base: '/',
notFound: {
path: '/404',
template: '404',
title: 'Page Not Found'
}
}
});Configuration Options
Main Options
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false | Enable router |
mode |
string | 'history' | Routing mode: 'hash' or 'history' |
base |
string | '/' | Application base path |
autoDetectBase |
boolean | true | Auto-detect base path from script location |
fallback |
string | 'index.html' | Fallback page for 404 |
showLoadingNotification |
boolean | true | Show loading notification |
Auth Configuration
auth: {
enabled: false, // Enable auth guards
autoGuard: true, // Auto-protect routes by conventions
defaultRequireAuth: false, // Require auth for all routes by default
publicPaths: ['/login', '/register'],
guestOnlyPaths: ['/login', '/register'],
conventions: {
'/auth/*': { public: true },
'/admin/*': { roles: ['admin'] }
},
redirects: {
unauthorized: '/login',
forbidden: '/403',
afterLogin: '/',
afterLogout: '/login'
}
}Not Found Configuration
notFound: {
path: '/404', // Route path for 404
template: '404', // Template for 404 page
title: 'Page Not Found',
behavior: 'render', // 'render', 'redirect', 'silent'
preserveUrl: false, // Preserve original URL
preserveQuery: true, // Preserve query string
customHandler: null // Custom handler function
}Initial Load Configuration
initialLoad: {
enabled: true, // Handle initial page load
preserveContent: false, // Preserve existing content
skipIfContent: true, // Skip if content exists
contentSelector: '#main', // Selector to check for content
forceRoute: false // Force route even with content
}Trailing Slash Configuration
trailingSlash: {
mode: 'remove', // 'remove', 'add', 'preserve'
redirect: false, // Redirect when handling trailing slash
ignorePaths: ['/'] // Paths to ignore trailing slash handling
}Methods and Properties
init(options)
Initialize RouterManager with configuration
Parameters:
options(Object, optional) - Configuration options
Return: (Promise) - Resolves when initialization completes
Usage Examples:
// Basic initialization
await RouterManager.init({
enabled: true,
mode: 'history'
});
// With auth enabled
await RouterManager.init({
enabled: true,
mode: 'history',
auth: {
enabled: true,
defaultRequireAuth: true,
publicPaths: ['/login', '/register', '/']
}
});
// With custom 404 handler
await RouterManager.init({
enabled: true,
notFound: {
behavior: 'redirect',
path: '/404',
customHandler: (path) => {
console.log('Page not found:', path);
}
}
});register(path, config)
Register a route in the router
Parameters:
path(string) - Route path supporting parameters like/user/:idconfig(Object|string) - Route configuration or template name
Configuration Object:
template(string) - Template path or HTML stringtitle(string, optional) - Page titleguards(Array, optional) - Route guardsrequireAuth(boolean, optional) - Require authenticationroles(Array, optional) - Required rolespublic(boolean, optional) - Public route (no auth needed)children(Object, optional) - Nested routes
Return: void
Usage Examples:
// Simple route
RouterManager.register('/home', 'home');
// Route with config
RouterManager.register('/about', {
template: 'about',
title: 'About Us'
});
// Route with parameters
RouterManager.register('/user/:id', {
template: 'user-profile',
title: 'User Profile'
});
// Protected route
RouterManager.register('/dashboard', {
template: 'dashboard',
title: 'Dashboard',
requireAuth: true
});
// Route with roles
RouterManager.register('/admin', {
template: 'admin',
title: 'Admin Panel',
requireAuth: true,
roles: ['admin']
});
// Route with inline template
RouterManager.register('/info', {
template: '<div><h1>Info Page</h1><p>Information content</p></div>',
title: 'Information'
});
// Route with nested routes
RouterManager.register('/docs', {
template: 'docs-layout',
title: 'Documentation',
children: {
'/getting-started': {
template: 'docs-getting-started',
title: 'Getting Started'
},
'/api': {
template: 'docs-api',
title: 'API Reference'
}
}
});navigate(path, params, options)
Navigate to specified route
Parameters:
path(string) - Route pathparams(Object, optional) - Route parametersoptions(Object, optional) - Navigation optionsreplace(boolean) - Use replaceState instead of pushStateskipGuards(boolean) - Skip guardspreserveQuery(boolean) - Preserve existing query string
Return: (Promise) - Resolves when navigation completes
Usage Examples:
// Basic navigation
await RouterManager.navigate('/home');
// Navigate with parameters
await RouterManager.navigate('/user/:id', { id: 123 });
// Navigate with query string
await RouterManager.navigate('/search?q=keyword');
// Replace current history entry
await RouterManager.navigate('/about', {}, { replace: true });
// Navigate programmatically
document.getElementById('btn').addEventListener('click', () => {
RouterManager.navigate('/contact');
});
// Navigate with params object
await RouterManager.navigate('/product/:category/:id', {
category: 'electronics',
id: 'laptop-001'
});
// Result: /product/electronics/laptop-001beforeEach(guard)
Add global guard that runs before every route navigation
Parameters:
guard(Function) - Guard function(to, from, next) => {}to(Object) - Target route informationfrom(Object) - Current route informationnext(Function) - Call to continue or pass path to redirect
Return: (Function) - Unregister function
Usage Examples:
// Log navigation
RouterManager.beforeEach((to, from, next) => {
console.log(`Navigating from ${from.path} to ${to.path}`);
next();
});
// Authentication guard
RouterManager.beforeEach(async (to, from, next) => {
const requireAuth = to.route?.requireAuth;
const isAuthenticated = await checkAuth();
if (requireAuth && !isAuthenticated) {
next('/login');
} else {
next();
}
});
// Role-based guard
RouterManager.beforeEach(async (to, from, next) => {
const requiredRoles = to.route?.roles;
if (requiredRoles) {
const userRoles = await getUserRoles();
const hasRole = requiredRoles.some(role => userRoles.includes(role));
if (!hasRole) {
next('/forbidden');
return;
}
}
next();
});
// Unregister guard
const unregister = RouterManager.beforeEach((to, from, next) => {
// Guard logic
next();
});
// Later...
unregister(); // Remove this guardafterEach(guard)
Add global guard that runs after every route navigation
Parameters:
guard(Function) - Guard function(to, from) => {}
Return: (Function) - Unregister function
Usage Examples:
// Page view tracking
RouterManager.afterEach((to, from) => {
if (window.gtag) {
gtag('config', 'GA_MEASUREMENT_ID', {
page_path: to.path
});
}
});
// Scroll to top
RouterManager.afterEach(() => {
window.scrollTo(0, 0);
});
// Update breadcrumb
RouterManager.afterEach((to) => {
updateBreadcrumb(to.path);
});
// Close mobile menu
RouterManager.afterEach(() => {
const mobileMenu = document.querySelector('.mobile-menu');
if (mobileMenu) {
mobileMenu.classList.remove('open');
}
});getPath(includeQuery)
Get current path
Parameters:
includeQuery(boolean, optional) - Include query string
Return: (string) - Current path
Usage Examples:
// Get path without query
const path = RouterManager.getPath();
console.log(path); // Output: /user/123
// Get path with query
const fullPath = RouterManager.getPath(true);
console.log(fullPath); // Output: /user/123?tab=profile
// Check current route
if (RouterManager.getPath() === '/admin') {
console.log('On admin page');
}getQuery()
Get current query string
Return: (string) - Query string including ?
Usage Examples:
// URL: /search?q=laptop&category=electronics
const query = RouterManager.getQuery();
console.log(query); // Output: ?q=laptop&category=electronics
// Parse query string
const params = new URLSearchParams(RouterManager.getQuery());
console.log(params.get('q')); // Output: laptop
console.log(params.get('category')); // Output: electronicsgetParams()
Get all route parameters
Return: (Object) - Parameters object
Usage Examples:
// URL: /user/123
// Route: /user/:id
const params = RouterManager.getParams();
console.log(params.id); // Output: 123
// URL: /product/electronics/laptop-001
// Route: /product/:category/:id
const params = RouterManager.getParams();
console.log(params.category); // Output: electronics
console.log(params.id); // Output: laptop-001getParam(name)
Get route parameter by name
Parameters:
name(string) - Parameter name
Return: (string|undefined) - Parameter value
Usage Examples:
// URL: /user/123
// Route: /user/:id
const userId = RouterManager.getParam('id');
console.log(userId); // Output: 123
// Check if parameter exists
const categoryId = RouterManager.getParam('categoryId');
if (categoryId) {
loadCategory(categoryId);
}hasRoute(path)
Check if route exists
Parameters:
path(string) - Route path
Return: (boolean)
Usage Examples:
if (RouterManager.hasRoute('/admin')) {
console.log('Admin route exists');
}
// Conditional navigation
const targetPath = '/dashboard';
if (RouterManager.hasRoute(targetPath)) {
RouterManager.navigate(targetPath);
} else {
RouterManager.navigate('/404');
}getRoute(path)
Get route configuration
Parameters:
path(string) - Route path
Return: (Object|undefined) - Route configuration
Usage Examples:
const route = RouterManager.getRoute('/dashboard');
console.log(route.title); // Output: Dashboard
console.log(route.requireAuth); // Output: true
// Check route properties
const aboutRoute = RouterManager.getRoute('/about');
if (aboutRoute) {
console.log('Template:', aboutRoute.template);
console.log('Title:', aboutRoute.title);
}getRoutes()
Get all routes
Return: (Array) - Array of route objects
Usage Examples:
const allRoutes = RouterManager.getRoutes();
allRoutes.forEach(route => {
console.log(`${route.path} - ${route.title}`);
});
// Generate navigation menu
const routes = RouterManager.getRoutes();
const menuItems = routes
.filter(route => !route.hidden)
.map(route => ({
path: route.path,
title: route.title
}));State Properties
RouterManager has a state object storing current state:
RouterManager.state = {
current: null, // Current route object
previous: null, // Previous route object
initialized: false, // Initialization status
loading: false, // Loading status
error: null, // Error object (if any)
disabled: false // Disabled status
}Usage Examples:
// Check if initialized
if (RouterManager.state.initialized) {
console.log('Router is ready');
}
// Check loading state
if (RouterManager.state.loading) {
console.log('Navigation in progress...');
}
// Check current route
console.log('Current route:', RouterManager.state.current?.path);
console.log('Previous route:', RouterManager.state.previous?.path);
// Check for errors
if (RouterManager.state.error) {
console.error('Router error:', RouterManager.state.error);
}Practical Examples
Example 1: Basic SPA Setup
// Initialize router
await RouterManager.init({
enabled: true,
mode: 'history',
base: '/'
});
// Register routes
RouterManager.register('/', {
template: 'home',
title: 'Home'
});
RouterManager.register('/about', {
template: 'about',
title: 'About Us'
});
RouterManager.register('/contact', {
template: 'contact',
title: 'Contact'
});
// HTML links
// <a href="/about">About</a>
// <a href="/contact">Contact</a>Example 2: Protected Routes with Authentication
// Initialize with auth
await RouterManager.init({
enabled: true,
auth: {
enabled: true,
defaultRequireAuth: false,
publicPaths: ['/login', '/register', '/'],
redirects: {
unauthorized: '/login'
}
}
});
// Public routes
RouterManager.register('/', {
template: 'home',
title: 'Home',
public: true
});
RouterManager.register('/login', {
template: 'login',
title: 'Login',
public: true
});
// Protected routes
RouterManager.register('/dashboard', {
template: 'dashboard',
title: 'Dashboard',
requireAuth: true
});
RouterManager.register('/profile', {
template: 'profile',
title: 'My Profile',
requireAuth: true
});
// Admin only route
RouterManager.register('/admin', {
template: 'admin',
title: 'Admin Panel',
requireAuth: true,
roles: ['admin']
});Example 3: Dynamic Routes with Parameters
// User profile route
RouterManager.register('/user/:id', {
template: 'user-profile',
title: 'User Profile'
});
// Product detail route
RouterManager.register('/product/:category/:id', {
template: 'product-detail',
title: 'Product Details'
});
// Blog post route
RouterManager.register('/blog/:slug', {
template: 'blog-post',
title: 'Blog Post'
});
// Navigate with params
RouterManager.navigate('/user/:id', { id: 123 });
RouterManager.navigate('/product/:category/:id', {
category: 'electronics',
id: 'laptop-001'
});
RouterManager.navigate('/blog/:slug', { slug: 'hello-world' });
// Access params in template
const userId = RouterManager.getParam('id');
const category = RouterManager.getParam('category');
const productId = RouterManager.getParam('id');Example 4: Custom Navigation Guards
// Loading indicator guard
RouterManager.beforeEach((to, from, next) => {
// Show loading
document.body.classList.add('loading');
next();
});
RouterManager.afterEach(() => {
// Hide loading
document.body.classList.remove('loading');
});
// Permission check guard
RouterManager.beforeEach(async (to, from, next) => {
if (to.route?.requireAuth) {
const authManager = Now.getManager('auth');
if (!authManager.isAuthenticated()) {
next('/login');
return;
}
if (to.route.roles) {
const hasPermission = await authManager.hasAnyRole(to.route.roles);
if (!hasPermission) {
next('/forbidden');
return;
}
}
}
next();
});
// Analytics tracking
RouterManager.afterEach((to, from) => {
// Google Analytics
if (window.gtag) {
gtag('config', 'GA_MEASUREMENT_ID', {
page_path: to.path,
page_title: to.route?.title
});
}
// Custom analytics
trackPageView(to.path);
});Example 5: Hash Mode Setup
// Initialize in hash mode
await RouterManager.init({
enabled: true,
mode: 'hash', // URLs will be like: /#/path
base: '/'
});
// Register routes (same as history mode)
RouterManager.register('/home', 'home');
RouterManager.register('/about', 'about');
// URLs will be:
// http://example.com/#/home
// http://example.com/#/about
// Navigation works the same
RouterManager.navigate('/home');Example 6: Custom 404 Handler
await RouterManager.init({
enabled: true,
notFound: {
behavior: 'render',
template: 'custom-404',
title: '404 - Page Not Found',
preserveUrl: true,
customHandler: (path) => {
console.log('404 - Path not found:', path);
// Log to analytics
if (window.gtag) {
gtag('event', 'page_not_found', {
page_path: path
});
}
// Suggest similar pages
suggestSimilarPages(path);
}
}
});Events and Callbacks
RouterManager works with EventManager by emitting various events:
router:before-navigate
Fired before navigation starts
Now.getManager('event').on('router:before-navigate', (data) => {
console.log('Navigating to:', data.path);
console.log('Parameters:', data.params);
});router:after-navigate
Fired after successful navigation
Now.getManager('event').on('router:after-navigate', (data) => {
console.log('Navigation complete');
console.log('Current route:', data.to);
console.log('Previous route:', data.from);
});router:error
Fired when navigation error occurs
Now.getManager('event').on('router:error', (error) => {
console.error('Navigation error:', error);
});router:not-found
Fired when route not found
Now.getManager('event').on('router:not-found', (data) => {
console.log('Route not found:', data.path);
});router:auth-redirect
Fired when redirected due to authentication
Now.getManager('event').on('router:auth-redirect', (data) => {
console.log('Auth redirect:', data.reason);
console.log('From:', data.from);
console.log('To:', data.to);
});Best Practices
✓ Do's
- ✓ Use history mode for production (cleaner URLs)
- ✓ Use hash mode for development or when server configuration is not possible
- ✓ Set correct base path when app is not at root
- ✓ Use route parameters for dynamic content
- ✓ Configure appropriate 404 handler
- ✓ Use guards for authentication and authorization
- ✓ Clearly separate public and protected routes
- ✓ Use meaningful template names
- ✓ Use async/await for navigation and guards
- ✓ Handle errors appropriately
- ✓ Use Events for tracking and analytics
✗ Don'ts
- ✗ Don't hard-code URLs in code, use
navigate()instead - ✗ Don't forget server rewrite configuration for history mode
- ✗ Don't use
window.location.hreffor navigation - ✗ Don't forget to call
next()in beforeEach guards - ✗ Don't block navigation too long in guards
- ✗ Don't use query strings for sensitive data
- ✗ Don't create circular redirects
- ✗ Don't forget to handle async operations in guards
Tips and Recommendations
-
Server Configuration for History Mode
- Apache: Use
.htaccessto redirect all requests toindex.html - Nginx: Use
try_files $uri $uri/ /index.html
- Apache: Use
-
Route Organization
- Group routes by feature
- Use nested routes for complex layouts
- Separate public and protected routes
-
Performance
- Use lazy loading for large templates
- Cache frequently used templates
- Optimize guards for fast execution
-
Security
- Use auth guards for protected routes
- Validate parameters in guards
- Don't rely solely on client-side routing
Common Pitfalls
❌ Wrong: Using window.location.href for navigation
// ❌ Wrong - causes page reload
document.querySelector('#link').addEventListener('click', () => {
window.location.href = '/about';
});✅ Right: Use RouterManager.navigate()
// ✅ Right - SPA navigation
document.querySelector('#link').addEventListener('click', () => {
RouterManager.navigate('/about');
});❌ Wrong: Forgetting to call next() in guard
// ❌ Wrong - Navigation will stop
RouterManager.beforeEach((to, from, next) => {
console.log('Navigating...');
// Forgot to call next()
});✅ Right: Always call next()
// ✅ Right
RouterManager.beforeEach((to, from, next) => {
console.log('Navigating...');
next(); // Must call
});❌ Wrong: History mode without server configuration
// ❌ Wrong - Direct URLs will cause 404 errors
await RouterManager.init({
mode: 'history'
});
// But no server rewrite rule✅ Right: Configure server rewrite
# .htaccess for Apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>// ✅ Right - Now direct URLs will work
await RouterManager.init({
mode: 'history'
});❌ Wrong: Blocking guard too long
// ❌ Wrong - Guard takes too long
RouterManager.beforeEach(async (to, from, next) => {
// Slow API call
await fetch('/api/slow-endpoint');
await new Promise(resolve => setTimeout(resolve, 5000));
next();
});✅ Right: Optimize guard performance
// ✅ Right - Do only what's necessary
RouterManager.beforeEach(async (to, from, next) => {
// Check cache first
if (isCached()) {
next();
return;
}
// Quick validation only
const isValid = await quickCheck();
next(isValid ? undefined : '/error');
});❌ Wrong: Not handling missing parameters
// ❌ Wrong - May cause undefined errors
RouterManager.register('/user/:id', {
template: 'user-profile'
});
// In template
const userId = RouterManager.getParam('id');
loadUser(userId); // userId might be undefined✅ Right: Validate parameters
// ✅ Right - Validate before use
RouterManager.register('/user/:id', {
template: 'user-profile'
});
const userId = RouterManager.getParam('id');
if (!userId) {
RouterManager.navigate('/404');
} else {
loadUser(userId);
}
// Or use guard
RouterManager.beforeEach((to, from, next) => {
if (to.path.startsWith('/user/') && !to.params.id) {
next('/404');
} else {
next();
}
});Performance Considerations
Performance Optimization
-
Template Caching
// Templates are cached automatically // But can be preloaded RouterManager.register('/dashboard', { template: 'dashboard', preload: true }); -
Lazy Loading
// Load template only when needed RouterManager.register('/heavy-page', { template: async () => { const module = await import('./templates/heavy-page.js'); return module.template; } }); -
Route Guards Optimization
// Cache auth check results let authCache = null; let cacheTime = 0; RouterManager.beforeEach(async (to, from, next) => { const now = Date.now(); // Use cache if fresh (5 seconds) if (authCache && (now - cacheTime < 5000)) { next(authCache ? undefined : '/login'); return; } // Check auth authCache = await checkAuth(); cacheTime = now; next(authCache ? undefined : '/login'); });
Considerations
- Navigation frequency: Don't navigate too frequently (debounce if needed)
- Guard complexity: Guards should be fast (< 100ms)
- Template size: Use lazy loading for large templates
- Memory leaks: Unregister event listeners when done
Security Considerations
Security Considerations
-
Client-Side Security
// ❌ Don't rely only on client-side protection RouterManager.register('/admin', { requireAuth: true, roles: ['admin'] }); // ✅ Must have server-side validation too // Server must verify authentication and authorization -
XSS Protection
// ✅ RouterManager escapes HTML automatically // But be careful when using innerHTML directly // ✓ Safe const params = RouterManager.getParams(); element.textContent = params.name; // ✗ Dangerous element.innerHTML = params.name; // Possible XSS -
Parameter Validation
// ✅ Validate parameters RouterManager.beforeEach((to, from, next) => { if (to.params.id) { // Validate ID format if (!/^\d+$/.test(to.params.id)) { next('/404'); return; } } next(); }); -
CSRF Protection
// For forms submitted via router // Use CSRF token RouterManager.beforeEach(async (to, from, next) => { if (to.route?.requiresCSRF) { const token = await getCSRFToken(); to.params._csrf = token; } next(); });
Browser Compatibility
Supported Browsers
| Browser | Version | Mode Support |
|---|---|---|
| Chrome | Latest 2 versions | Hash, History |
| Firefox | Latest 2 versions | Hash, History |
| Safari | Latest 2 versions | Hash, History |
| Edge | Latest 2 versions | Hash, History |
| iOS Safari | iOS 12+ | Hash, History |
| Chrome Android | Latest | Hash, History |
Polyfills
For older browsers:
<!-- History API polyfill -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=URL,history"></script>Known Issues
-
iOS Safari Back Button: May have issues with history.back() on older iOS Safari
- Solution: Use hash mode or update iOS
-
IE11: Partial History API support
- Solution: Use hash mode or polyfill
Related Classes/Methods
TemplateManager
- Works with RouterManager to load and display templates
- TemplateManager Documentation
AuthManager
- Used for authentication guards
- Integration for protected routes
- AuthManager Documentation
EventManager
- RouterManager emits events via EventManager
- EventManager Documentation
HistoryManager
- Manages browser history
- Back/Forward navigation
Additional Notes
Supported Versions
- Now.js v1.0+
- ES6+ JavaScript
Breaking Changes
v1.0:
- Changed API from callback-based to Promise-based
init()must be awaited- Guards support async/await
Deprecation Notices
RouterManager.route()- Useregister()insteadRouterManager.go()- Usenavigate()instead
Limitations
- Router works client-side only
- Server configuration needed for history mode
- SEO: Use SSR or prerendering for better SEO
- Query parameters have length limits per browser
API Reference
Complete methods list:
| Method | Parameters | Return | Description |
|---|---|---|---|
init(options) |
Object | Promise | Initialize router |
register(path, config) |
String, Object | void | Register route |
navigate(path, params, options) |
String, Object, Object | Promise | Navigate to route |
beforeEach(guard) |
Function | Function | Add before guard |
afterEach(guard) |
Function | Function | Add after guard |
getPath(includeQuery) |
Boolean | String | Get current path |
getQuery() |
- | String | Get query string |
getParams() |
- | Object | Get all parameters |
getParam(name) |
String | String | Get parameter by name |
hasRoute(path) |
String | Boolean | Check if route exists |
getRoute(path) |
String | Object | Get route config |
getRoutes() |
- | Array | Get all routes |
Complete Project Example
// main.js - SPA with authentication
(async () => {
// Initialize Now.js with Router
await Now.init({
router: {
enabled: true,
mode: 'history',
base: '/',
auth: {
enabled: true,
defaultRequireAuth: false,
publicPaths: ['/login', '/register', '/'],
redirects: {
unauthorized: '/login',
afterLogin: '/dashboard'
}
},
notFound: {
template: '404',
title: 'Page Not Found'
}
}
});
const router = Now.getManager('router');
// Public routes
router.register('/', {
template: 'home',
title: 'Home',
public: true
});
router.register('/login', {
template: 'login',
title: 'Login',
public: true
});
router.register('/register', {
template: 'register',
title: 'Register',
public: true
});
// Protected routes
router.register('/dashboard', {
template: 'dashboard',
title: 'Dashboard',
requireAuth: true
});
router.register('/profile', {
template: 'profile',
title: 'My Profile',
requireAuth: true
});
router.register('/settings', {
template: 'settings',
title: 'Settings',
requireAuth: true
});
// Admin routes
router.register('/admin', {
template: 'admin',
title: 'Admin Panel',
requireAuth: true,
roles: ['admin']
});
// Dynamic routes
router.register('/user/:id', {
template: 'user-profile',
title: 'User Profile',
requireAuth: true
});
// Global guards
router.beforeEach((to, from, next) => {
console.log(`Navigating: ${from.path} → ${to.path}`);
next();
});
router.afterEach((to, from) => {
// Analytics
if (window.gtag) {
gtag('config', 'GA_ID', {
page_path: to.path
});
}
// Scroll to top
window.scrollTo(0, 0);
});
})();