{"_id":"@dera-ez/csr-router-js","_rev":"2-a863c1afbc9ef4ddcecb138e245bb120","name":"@dera-ez/csr-router-js","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@dera-ez/csr-router-js","version":"1.0.0","keywords":["router","client-side-router","spa","vanilla-js","file-based-routing","navigation","lightweight"],"author":"","license":"ISC","_id":"@dera-ez/csr-router-js@1.0.0","maintainers":[{"name":"dera-ez","email":"derajatul@gmail.com"}],"homepage":"https://github.com/Dera/simple-router-js#readme","bugs":{"url":"https://github.com/Dera/simple-router-js/issues"},"dist":{"shasum":"02f4a2c4850b6de347a0fefcd237c738b8592c94","tarball":"https://registry.npmjs.org/@dera-ez/csr-router-js/-/csr-router-js-1.0.0.tgz","fileCount":6,"integrity":"sha512-idfiLJ4vfvGusL0ISvtQF/A3gRjensCvd6+z0rB2M+zONxf48r8QdDacDXI4S5UMuUf8ZznLYyL5WmgiGwByLg==","signatures":[{"sig":"MEQCICg53RpnmWabaBV8hV/wfm452WcLvzGmOe7/FTN403K0AiAvIFGP+sQA4jf6ogL3UsJYK32Q39lGXzIoEIwWU3FwGA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27139},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"9e5d02744fdae6800d2e15c49402c1650d7217f8","scripts":{"test":"vitest run","build":"tsup src/index.ts --format esm,cjs --dts","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run test && npm run build"},"_npmUser":{"name":"dera-ez","email":"derajatul@gmail.com"},"repository":{"url":"git+https://github.com/Dera/simple-router-js.git","type":"git"},"_npmVersion":"11.13.0","description":"A lightweight, zero-dependency client-side router for vanilla JavaScript and TypeScript projects with file-based routing.","directories":{},"sideEffects":false,"_nodeVersion":"26.2.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^29.1.1","vitest":"^4.1.9","typescript":"^6.0.3","@vitest/coverage-v8":"^4.1.9"},"_npmOperationalInternal":{"tmp":"tmp/csr-router-js_1.0.0_1782727746118_0.12720912083833147","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@dera-ez/csr-router-js","version":"1.0.1","description":"A lightweight, zero-dependency client-side router for vanilla JavaScript and TypeScript projects with file-based routing.","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"sideEffects":false,"scripts":{"build":"tsup src/index.ts --format esm,cjs --dts","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run test && npm run build"},"keywords":["router","client-side-router","spa","vanilla-js","file-based-routing","navigation","lightweight"],"author":"","license":"ISC","repository":{"type":"git","url":"git+https://github.com/Dera/simple-router-js.git"},"homepage":"https://github.com/Dera/simple-router-js#readme","bugs":{"url":"https://github.com/Dera/simple-router-js/issues"},"devDependencies":{"@vitest/coverage-v8":"^4.1.9","jsdom":"^29.1.1","tsup":"^8.5.1","typescript":"^6.0.3","vitest":"^4.1.9"},"gitHead":"9e5d02744fdae6800d2e15c49402c1650d7217f8","_id":"@dera-ez/csr-router-js@1.0.1","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-jOIzsRv5zxlfs+8rSW46AwQuzvwzSKFGiannJiN6RXObcFVu7MBm6HRpGDgGykRDrE+nM0pHlykCSlcF7j6x5g==","shasum":"9606286adaec15998f89f3a149824641d6af4816","tarball":"https://registry.npmjs.org/@dera-ez/csr-router-js/-/csr-router-js-1.0.1.tgz","fileCount":6,"unpackedSize":28047,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC5fHOOLzl2cXXJddIsAj3IB6gN3TZquJ0DRM1Ilyw68gIgSTn556PXGZ8a0JRJds0LD6ZVb29H8WN3b+JrhT8bcLc="}]},"_npmUser":{"name":"dera-ez","email":"derajatul@gmail.com"},"directories":{},"maintainers":[{"name":"dera-ez","email":"derajatul@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/csr-router-js_1.0.1_1782730415658_0.8098601596289972"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-29T10:09:05.945Z","modified":"2026-06-29T10:53:35.931Z","1.0.0":"2026-06-29T10:09:06.259Z","1.0.1":"2026-06-29T10:53:35.815Z"},"bugs":{"url":"https://github.com/Dera/simple-router-js/issues"},"license":"ISC","homepage":"https://github.com/Dera/simple-router-js#readme","keywords":["router","client-side-router","spa","vanilla-js","file-based-routing","navigation","lightweight"],"repository":{"type":"git","url":"git+https://github.com/Dera/simple-router-js.git"},"description":"A lightweight, zero-dependency client-side router for vanilla JavaScript and TypeScript projects with file-based routing.","maintainers":[{"name":"dera-ez","email":"derajatul@gmail.com"}],"readme":"# Simple Router\n\nA lightweight, zero-dependency client-side router for vanilla JavaScript and TypeScript projects. It uses **file-based routing** — just create `.html` files and the router loads them automatically via `fetch`. No build step required for your pages.\n\n## Features\n\n- **File-based routing** — maps URL paths to `.html` files automatically\n- **Dynamic routes** — define routes with parameters like `/user/:id`\n- **Navigation guards** — control access with `beforeEach` hooks\n- **Active link highlighting** — automatically marks the current nav link\n- **Script execution** — inline `<script>` tags in loaded pages are executed\n- **404 fallback** — customizable fallback page when a route is not found\n- **Title extraction** — automatically updates `document.title` from `<title>` tags\n- **Tiny footprint** — no dependencies, ~6 KB (ESM)\n\n\n## Installation\n\n### Via npm\n\n```bash\nnpm install @dera-ez/csr-router-js\n```\n\n### Via CDN (Directly in Browser)\n\nYou can import the module directly from a CDN like [esm.run](https://esm.run):\n\n```html\n<script type=\"module\">\n  import Router from 'https://esm.run/@dera-ez/csr-router-js';\n</script>\n```\n\n### Manual Copy\n\nCopy the `dist/` folder into your project and import the ES module directly:\n\n```html\n<script type=\"module\">\n  import Router from './dist/index.mjs';\n</script>\n```\n\n> [!WARNING]\n> **Local Server Required**: Because this library fetches HTML templates dynamically using the `fetch` API, browsers will block requests if you open your project directly from the filesystem using the `file://` protocol (CORS restriction). You **must** run your project using a local development server (such as `npx serve`, VS Code's Live Server, Vite, etc.).\n\n## Quick Start\n\n### 1. Create your page files\n\nCreate a `pages/` directory with HTML files:\n\n```\npages/\n├── index.html      ← /\n├── about.html      ← /about\n├── contact.html    ← /contact\n└── 404.html        ← fallback page\n```\n\nEach file contains **only the page content** (not a full HTML document):\n\n```html\n<!-- pages/index.html -->\n<title>Home Page</title>\n<h1>Welcome</h1>\n<p>This is the home page.</p>\n```\n\n### 2. Set up your HTML\n\nFor a browser-native setup (without a bundler), serve your files using a local server and reference the script:\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>My App</title>\n</head>\n<body>\n    <nav>\n        <a href=\"/\" data-link>Home</a>\n        <a href=\"/about\" data-link>About</a>\n        <a href=\"/contact\" data-link>Contact</a>\n    </nav>\n\n    <div id=\"app\"></div>\n\n    <script type=\"module\">\n        // Import from CDN or local copy\n        import Router from 'https://esm.run/@dera-ez/csr-router-js';\n\n        Router.init({\n            pagesDir: './pages'\n        });\n    </script>\n</body>\n</html>\n```\n\nIf you are using a bundler (Vite, Webpack, etc.):\n\n```js\nimport Router from '@dera-ez/csr-router-js';\n\nRouter.init({\n    pagesDir: './pages'\n});\n```\n\nThat's it! Clicking the `data-link` anchors will load the corresponding pages into `#app` without a full page reload.\n\n## Configuration\n\nPass a config object to `Router.init()`:\n\n```js\nRouter.init({\n    pagesDir: './pages',        // directory containing your .html page files\n    contentElId: 'app',         // id of the element where pages are rendered\n    fallbackPage: '404.html',   // filename of the fallback page\n    activeClass: 'active',      // CSS class added to the active nav link\n    routes: [],                 // dynamic route definitions (see below)\n});\n```\n\nAll options are optional. The values shown above are the defaults.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `pagesDir` | `string` | `\"/pages\"` | Path to the directory containing page `.html` files |\n| `contentElId` | `string` | `\"app\"` | The `id` of the DOM element where page content is injected |\n| `fallbackPage` | `string` | `\"404.html\"` | Filename of the fallback page (relative to `pagesDir`) |\n| `activeClass` | `string` | `\"active\"` | CSS class name applied to the active navigation link |\n| `routes` | `RouteDefinition[]` | `[]` | Array of dynamic route definitions |\n\n## File-Based Routing\n\nBy default, the router maps URL paths to `.html` files inside `pagesDir`:\n\n| URL Path | File Loaded |\n|---|---|\n| `/` | `{pagesDir}/index.html` |\n| `/about` | `{pagesDir}/about.html` |\n| `/blog/hello` | `{pagesDir}/blog/hello.html` |\n\nNo configuration needed — just create the files and they're routable.\n\n## Dynamic Routes\n\nFor URLs with parameters (e.g. `/user/123`), define routes with `:param` placeholders:\n\n```js\nRouter.init({\n    pagesDir: './pages',\n    routes: [\n        { path: '/user/:id', template: 'user-detail.html' },\n        { path: '/blog/:category/:slug', template: 'blog-post.html' },\n    ],\n});\n```\n\n### Accessing Parameters\n\nAfter navigation, route parameters are available via `Router.params`:\n\n```js\n// After navigating to /user/456\nconsole.log(Router.params); // { id: '456' }\n\n// After navigating to /blog/tech/hello-world\nconsole.log(Router.params); // { category: 'tech', slug: 'hello-world' }\n```\n\n### Template Resolution\n\n- **Relative templates** (e.g. `'user-detail.html'`) are resolved relative to `pagesDir`.\n- **Absolute templates** (e.g. `'/custom/special.html'`) are used as-is.\n\nIf no dynamic route matches, the router falls back to file-based routing.\n\n## Navigation\n\n### Declarative (HTML)\n\nAdd the `data-link` attribute to any anchor element. The router intercepts clicks on these links and navigates without a full page reload:\n\n```html\n<a href=\"/about\" data-link>About</a>\n```\n\n### Programmatic (JavaScript)\n\nUse `Router.navigate()` to navigate from code:\n\n```js\nRouter.navigate('/about');\n```\n\n### Back/Forward\n\nThe router automatically handles browser back/forward buttons via the `popstate` event.\n\n## Navigation Guards\n\nUse `Router.beforeEach()` to register guards that run before every navigation. Guards receive three arguments:\n\n| Argument | Type | Description |\n|---|---|---|\n| `to` | `string` | The target path |\n| `from` | `string` | The current path |\n| `next` | `function` | Callback to control navigation |\n\n### Calling `next()`\n\n| Call | Effect |\n|---|---|\n| `next()` | Allow navigation |\n| `next(false)` | Block navigation |\n| `next('/login')` | Redirect to another path |\n\n### Example: Auth Guard\n\n```js\nRouter.init({ pagesDir: './pages' });\n\nRouter.beforeEach((to, from, next) => {\n    const isAuthenticated = !!localStorage.getItem('token');\n\n    if (to === '/dashboard' && !isAuthenticated) {\n        next('/login'); // redirect to login\n    } else {\n        next(); // allow\n    }\n});\n```\n\n### Multiple Guards\n\nYou can register multiple guards. They run in order, and the chain stops if any guard blocks or redirects:\n\n```js\nRouter.beforeEach((to, from, next) => {\n    console.log('Guard 1');\n    next();\n});\n\nRouter.beforeEach((to, from, next) => {\n    console.log('Guard 2');\n    next();\n});\n```\n\n> **Note:** Guards are reset when `Router.init()` is called again.\n\n## Active Link Highlighting\n\nThe router automatically adds a CSS class to navigation links (`[data-link]`) that match the current URL path. By default the class is `active`, but you can customize it:\n\n```js\nRouter.init({ activeClass: 'current' });\n```\n\n```css\nnav a.active {\n    font-weight: bold;\n    color: #ff6600;\n}\n```\n\n## Page Title\n\nIf a loaded page contains a `<title>` tag, the router automatically updates `document.title`:\n\n```html\n<!-- pages/about.html -->\n<title>About Us</title>\n<h1>About Us</h1>\n<p>Learn more about our team.</p>\n```\n\nNavigating to `/about` will set the browser tab title to \"About Us\".\n\n## Inline Scripts\n\nInline `<script>` tags inside loaded pages are automatically executed. Script elements are removed from the DOM after execution to keep the markup clean:\n\n```html\n<!-- pages/dashboard.html -->\n<h1>Dashboard</h1>\n<script>\n    console.log('Dashboard page loaded!');\n</script>\n```\n\n## 404 Fallback\n\nWhen a page file can't be fetched, the router tries to load the `fallbackPage` (default: `404.html` inside `pagesDir`). If that also fails, a built-in fallback message is shown:\n\n```\n404 Not Found\nThe page you requested could not be found.\n```\n\nCustomize by creating your own fallback page:\n\n```html\n<!-- pages/404.html -->\n<h1>404 - Page Not Found</h1>\n<p>Sorry, the page you're looking for doesn't exist.</p>\n<a href=\"/\" data-link>Go back home</a>\n```\n\n## API Reference\n\n### `Router.init(config?)`\n\nInitialize the router with optional configuration. Sets up event listeners for `data-link` clicks and `popstate` events, then renders the current page.\n\n### `Router.navigate(path)`\n\nProgrammatically navigate to a path. Runs navigation guards before navigating.\n\n### `Router.render()`\n\nManually re-render the current page. Typically you don't need to call this directly.\n\n### `Router.beforeEach(guard)`\n\nRegister a navigation guard function. See [Navigation Guards](#navigation-guards).\n\n### `Router.loadPage(url)`\n\nFetch and return the text content of a page. Returns a `Promise<string>`. Throws an error if the fetch fails.\n\n```js\nconst html = await Router.loadPage('/pages/about.html');\nconsole.log(html);\n```\n\n### `Router.params`\n\nA `Record<string, string>` containing the matched dynamic route parameters. Empty (`{}`) when no dynamic route matched.\n\n### `Router.config`\n\nThe current router configuration object (read-only).\n\n## TypeScript\n\nThe library ships with full type declarations. Key types:\n\n```ts\ninterface RouteDefinition {\n    path: string;\n    template: string;\n}\n\ninterface RouterConfig {\n    pagesDir?: string;\n    contentElId?: string;\n    fallbackPage?: string;\n    routes?: RouteDefinition[];\n    activeClass?: string;\n}\n\ntype NextCallback = (action?: boolean | string) => void;\ntype NavigationGuard = (to: string, from: string, next: NextCallback) => void;\n```\n\n## Building from Source\n\n```bash\n# Install dependencies\nnpm install\n\n# Build (outputs to dist/)\nnpm run build\n```\n\n## License\n\nISC\n","readmeFilename":"README.md"}