Skip to content

TarekRaafat/eleva-router

Repository files navigation

Eleva Router

License GitHub package.json version Version 100% Javascript Zero Dependencies Minified Size Gzipped Size

The official router plugin for Eleva.js - a lightweight, zero-dependency client-side routing solution with support for hash, query, and history modes.

Latest: v1.2.0-alpha with configurable view selectors, dynamic route parameters, enhanced error handling, and memory management.

✨ Features

  • πŸš€ Multiple routing modes: Hash (#/page), Query (?page=name), History (/page)
  • 🎯 Dynamic route parameters: /users/:id, /files/:path* (catch-all)
  • πŸ”§ Zero configuration: Works out of the box with sensible defaults
  • πŸ’Ύ Memory efficient: Automatic cleanup and leak prevention
  • πŸ›‘οΈ Error resilient: Built-in error handling and recovery
  • πŸ“¦ Tiny footprint: Zero dependencies, minimal bundle size

πŸš€ Quick Start

Installation

npm install eleva-router

Basic Usage

import Eleva from "eleva";
import ElevaRouter from "eleva-router";

const app = new Eleva("MyApp");

// Define components
const Home = {
  template: () => `<h1>Welcome Home!</h1>`,
};

const About = {
  setup: ({ navigate }) => ({
    goHome: () => navigate("/"),
  }),
  template: (ctx) => `
    <div>
      <h1>About Us</h1>
      <button @click="goHome">Go Home</button>
    </div>
  `,
};

// Setup router
app.use(ElevaRouter, {
  layout: document.getElementById("app"),
  mode: "history", // "hash" | "query" | "history"
  routes: [
    { path: "/", component: Home },
    { path: "/about", component: About },
  ],
});

πŸ—οΈ App Layout & View Element

The router uses an app layout concept where you provide a layout element that contains a dedicated view element for mounting routed components. This allows you to maintain persistent layout elements (like navigation, headers, footers) while only the view content changes during navigation.

The router automatically looks for a view element within your layout using these selectors (in order of priority, based on selection speed):

  1. #view - Element with view id (fastest - ID selector)
  2. .view - Element with view class (fast - class selector)
  3. <view> - Native <view> HTML element (medium - tag selector)
  4. [data-view] - Element with data-view attribute (slowest - attribute selector)
  5. Falls back to the layout element itself if no view element is found

Note: The difference in selection speed between these selector types is negligible for most practical cases. This ordering is a micro-optimization that may provide minimal performance benefits in applications with very frequent route changes.

Custom View Selectors

You can customize the view element selector by setting the viewSelector option:

// Using custom view selector
app.use(ElevaRouter, {
  layout: document.getElementById("app"),
  viewSelector: "router-view", // Custom selector name
  routes: [...]
});

This will look for elements like:

  • #router-view (ID)
  • .router-view (class)
  • <router-view> (tag)
  • data-router-view (attribute)

Example HTML Structure:

<div id="app">
  <header>
    <nav><a href="#/">Home</a> <a href="#/about">About</a></nav>
  </header>
  <main id="view"></main>
  <!-- Router mounts components here -->
  <footer>&copy; 2024</footer>
</div>

🎯 Dynamic Routes

// Route with parameters
{ path: "/users/:id", component: UserProfile }

// Catch-all route
{ path: "/files/:path*", component: FileViewer }

// Access parameters in component
const UserProfile = {
  setup: ({ route }) => ({
    userId: route.params.id // "123" for "/users/123"
  }),
  template: (ctx) => `<h1>User: ${ctx.userId}</h1>`
};

πŸ”§ Configuration

Option Type Default Description
layout HTMLElement required App layout element. Router looks for a view element (#{viewSelector}, .{viewSelector}, <{viewSelector}>, or data-{viewSelector}) within this layout to mount components. Priority based on selection speed (micro-optimization). If no view element is found, the layout itself is used.
mode string "hash" Routing mode: "hash", "query", or "history"
queryParam string "page" Query parameter name for query mode (?page=about vs ?view=about)
viewSelector string "view" Selector name for the view element. Used to find elements like #router-view, .router-view, <router-view>, or data-router-view.
routes array [] Array of route objects
defaultRoute object null Fallback route for unmatched paths
autoStart boolean true Auto-start router after installation

πŸ“± Navigation

From Components

const MyComponent = {
  setup: ({ navigate, route }) => ({
    // Simple navigation
    goToAbout: () => navigate("/about"),

    // With parameters
    goToUser: (id) => navigate("/users/:id", { id }),

    // Current route info
    currentPath: route.path,
    routeParams: route.params,
    queryParams: route.query,
  }),
};

Programmatically

// Navigate from anywhere
await app.router.navigate("/about");
await app.router.navigate("/users/:id", { id: 123 });

// Router control
await app.router.start(); // Manual start
await app.router.destroy(); // Cleanup

πŸ› οΈ Routing Modes

Hash Mode (Default)

// URLs: http://example.com/#/about
app.use(ElevaRouter, { mode: "hash", ... });

History Mode

// URLs: http://example.com/about
app.use(ElevaRouter, { mode: "history", ... });

Query Mode

// Default: ?page=about
app.use(ElevaRouter, { mode: "query", ... });

// Custom: ?view=about
app.use(ElevaRouter, {
  mode: "query",
  queryParam: "view",
  ...
});

πŸ” Examples

Query Mode Customization
// E-commerce with custom parameter
app.use(ElevaRouter, {
  layout: document.getElementById("app"),
  mode: "query",
  queryParam: "category", // ?category=electronics
  routes: [
    { path: "/", component: Home },
    { path: "/electronics", component: Electronics },
    { path: "/books", component: Books },
  ],
});

// Admin panel
app.use(ElevaRouter, {
  layout: document.getElementById("app"),
  mode: "query",
  queryParam: "section", // ?section=users
  routes: [
    { path: "/", component: Dashboard },
    { path: "/users", component: UserManagement },
    { path: "/settings", component: Settings },
  ],
});
Complete App Example
import Eleva from "eleva";
import ElevaRouter from "eleva-router";

const app = new Eleva("BlogApp");

const routes = [
  {
    path: "/",
    component: {
      template: () => `<h1>Blog Home</h1>`,
    },
  },
  {
    path: "/posts/:id",
    component: {
      setup: ({ route, navigate }) => ({
        postId: route.params.id,
        goHome: () => navigate("/"),
      }),
      template: (ctx) => `
        <article>
          <h1>Post #${ctx.postId}</h1>
          <button @click="goHome">← Back</button>
        </article>
      `,
    },
  },
  {
    path: "/category/:name",
    component: {
      setup: ({ route }) => ({
        category: route.params.name,
      }),
      template: (ctx) => `<h1>Category: ${ctx.category}</h1>`,
    },
  },
];

app.use(ElevaRouter, {
  layout: document.getElementById("app"),
  mode: "history",
  routes,
  defaultRoute: {
    path: "/404",
    component: {
      template: () => `<h1>Page Not Found</h1>`,
    },
  },
});
Manual Router Control
app.use(ElevaRouter, {
  layout: document.getElementById("app"),
  routes: [...],
  autoStart: false // Don't start automatically
});

// Start when ready
document.addEventListener("DOMContentLoaded", async () => {
  try {
    await app.router.start();
    console.log("Router started!");
  } catch (error) {
    console.error("Router failed:", error);
  }
});

// Cleanup on exit
window.addEventListener("beforeunload", () => {
  app.router.destroy();
});

πŸ“š Documentation

For comprehensive documentation, advanced features, and best practices:

πŸ“– Full Documentation

  • Complete API reference
  • Advanced routing patterns
  • Error handling strategies
  • Performance optimization
  • Migration guides

πŸ› Troubleshooting

Common Issues:

  • Routes not matching: Check path syntax and ensure layout exists
  • Components not mounting: Verify component definitions and layout element
  • Navigation not working: Use navigate() from context or app.router.navigate()
  • Memory issues: Call app.router.destroy() during cleanup

Need Help?

🀝 Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

πŸ“„ License

MIT License - feel free to use in any project.


Made with πŸ–€ for the Eleva.js ecosystem

About

A Router Plugin for Eleva.js

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published