@piyushagade/flexirouter
v1.0.2
Published
A lightweight, feature-rich SPA router supporting Hash/Path modes, dynamic params, and an event bus.
Maintainers
Readme
@piyushagade/flexirouter 🚀
A lightweight, feature-loaded Single Page Application (SPA) router for jQuery-powered projects. FlexiRouter transforms your traditional multi-page website into a modern, fluid experience with zero-config hash routing or clean Path URLs.
✨ Features
- Dual Mode: Switch between clean Path URLs (History API) and Zero-Config Hash URLs (
#/path). - Dynamic Parameters: Pattern matching to extract variables from URLs (e.g.,
/user/:id). - Lifecycle Hooks: Granular control with
onLoad,onClose, andonRefresh. - Lazy Loading: Dynamic imports for modules, loading code only when the route is active.
- Route Guards: Global
authGuardand per-routebeforeEntermiddleware. - Event Bus: A built-in Pub/Sub system for decoupled module communication.
- Headless Breadcrumbs: Returns an array of "Steps" on every route change for custom UI rendering.
- Loading States: Built-in management for async loading overlays.
✨ New in v1.0.1
- ✅ Auto-Queueing: Call
router.navigate()insideonLoadwithoutsetTimeout. The router now queues nested calls automatically. - 🎣 Lifecycle Fixes:
onCloseandonRefreshnow fire reliably with standardized arguments. - 🛡️ MIME Safety: Documentation updated to address nested path MIME type errors via absolute paths.
📦 Installation
npm install @piyushagade/flexirouter<script type="module" src="https://cdn.jsdelivr.net/npm/@piyushagade/flexirouter@latest/FlexiRouter.min.js"></script>🚯 Quick Start
1. Define your HTML
Mark your views with a specific class or data attribute (default is [data-route-view]). The dafault data attribute for navigation links is [data-link]. Both attribute names can be configured.
<!-- Navigation -->
<nav>
<a href="/dashboard" data-link>Dashboard</a>
<a href="/settings" data-link>Settings</a>
</nav>
<!-- Views -->
<div id="view-dashboard" data-route-view class="hidden">Dashboard Content</div>
<div id="view-settings" data-route-view class="hidden">Settings Content</div>
<!-- Optional Loader -->
<div id="router-loader" style="display:none;">Loading...</div>2. Initialize the Router
import FlexiRouter from '@piyushagade/flexirouter';
new FlexiRouter({
useHash: true,
routes: {
'/': { redirectTo: '/dashboard' },
'/dashboard': {
target: 'view-dashboard',
steps: [{ name: 'Home', url: '/' }, { name: 'Dashboard', url: '/dashboard' }]
},
'/user/:username': {
target: 'view-profile',
module: './ProfileManager.js',
initMethod: 'init'
},
'/printer/:id': {
onLoad: ({ params }) => {
console.log("Entering printer", params.id);
},
onClose: () => console.log("Exiting printer")
}
}
});Another example:
const router = new FlexiRouter({
routes: {
'/': {
onLoad: (router) => router.navigate('/dashboard')
},
'/profile/:id': {
onLoad: (router, { params }) => console.log("Entering", params.id),
onClose: () => console.log("Cleaning up")
}
}
});🛡️ Route Middleware & Guards
const router = new FlexiRouter({
authGuard: async () => {
return !!localStorage.getItem('token');
},
routes: {
'/admin': {
target: 'admin-view',
protected: true,
beforeEnter: async (params) => confirm("Enter restricted area?")
}
}
});🎣 Lifecycle Hooks
If you do not wish to use FlexiRouter as described in Define your HTML section above, you can manually control DOM by bypassing the target property and using the following functions instead:
- onLoad: Triggered when entering a route.
- onClose: Triggered when leaving a route for a new one.
- onRefresh: Triggered when navigating to the already active route.
const router = new FlexiRouter({
authGuard: async () => {
return !!localStorage.getItem('token');
},
routes: {
'/': {
onLoad: (router, { params }) => {
if (!auth.loggedin) router.navigate('/login');
else router.navigate('/profile/' + auth.userId)
},
onClose: (router, { params }) => {
console.log('Leaving login view');
},
},
'/profile/:id': {
onLoad: (router, { params }) => {
console.log('User profile loaded for: ' + auth.userId);
// DOM manipulation
$('#login-overlay').fadeOut(200);
$('#member-overlay').fadeIn(100);
loadUserData(auth.userId);
},
}
}
});📢 Event Bus
router.on('user-login', (user) => console.log(user.name));
router.emit('user-login', { name: 'Piyush' });🌐 Server Configurations
For Path Mode (useHash: false), your server must serve index.html for all routes to allow FlexiRouter to take over routing.
Express.js
// Serve static files first, then fallback to index.html for non-API routes
app.use(express.static('public'));
app.get(/^(?!\/api).+/, (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});Flask (Python)
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
return render_template('index.html')Nginx
location / {
try_files $uri $uri/ /index.html;
}🛠️ Migration Guide
- Consolidate: Move body content into
index.htmlwithindata-route-viewdivs. - Links: Add
data-linkto anchor tags. - Logic: Move jQuery logic into ES Modules.
- Server: For Path mode, redirect 404s to
index.html.
📄 License
MIT © Piyush Agade
