Vite Plugin: Automatic File-based Routing 🚦
SigPro provides an optional Vite plugin that automatically generates routes based on your file structure. No configuration needed - just create pages and they're instantly available with the correct paths.
Why Use This Plugin?
While SigPro's router works perfectly with manually defined routes, this plugin:
- Eliminates boilerplate - No need to write route configurations
- Enforces conventions - Consistent URL structure across your app
- Supports dynamic routes - Use
[param]syntax for parameters - Automatic code-splitting - Each page becomes a separate chunk
- Type-safe (with JSDoc) - Routes follow your file structure
Installation
The plugin is included with SigPro, but you need to add it to your Vite config:
// vite.config.js
import { defineConfig } from 'vite';
import { sigproRouter } from 'sigpro';
export default defineConfig({
plugins: [sigproRouter()]
});How It Works
The plugin scans your src/pages directory and automatically generates routes based on the file structure:
src/pages/
├── index.js → '/'
├── about.js → '/about'
├── blog/
│ ├── index.js → '/blog'
│ └── [slug].js → '/blog/:slug'
└── users/
├── [id].js → '/users/:id'
└── [id]/edit.js → '/users/:id/edit'Usage
1. Enable the Plugin
Add the plugin to your Vite config as shown above.
2. Import the Generated Routes
Once you have the generated routes, using them with the router is straightforward:
// main.js
import { $, html } from 'sigpro';
import { routes } from 'virtual:sigpro-routes';
// Simple usage
const router = $.router(routes);
document.body.appendChild(router);Or directly in your template:
// app.js
import { $, html } from 'sigpro';
import { routes } from 'virtual:sigpro-routes';
const App = () => html`
<div class="app">
<header>
<h1>My Application</h1>
</header>
<main class="p-4 flex flex-col gap-4 mx-auto w-full">
<div class="p-4 bg-base-100 rounded-box shadow-sm">
${$.router(routes)}
</div>
</main>
</div>
`;
document.body.appendChild(App());This approach keeps your template clean and lets the router handle all the page rendering automatically.
3. Create Pages
// src/pages/index.js
import { $, html } from 'sigpro';
export default () => {
return html`
<div>
<h1>Home Page</h1>
<a href="#/about">About</a>
</div>
`;
};// src/pages/users/[id].js
import { $, html } from 'sigpro';
export default (params) => {
const userId = params.id;
return html`
<div>
<h1>User Profile: ${userId}</h1>
<a href="#/users/${userId}/edit">Edit</a>
</div>
`;
};📋 File-to-Route Mapping
Static Routes
| File Path | Generated Route |
|---|---|
src/pages/index.js | / |
src/pages/about.js | /about |
src/pages/contact/index.js | /contact |
src/pages/blog/post.js | /blog/post |
Dynamic Routes
| File Path | Generated Route | Example URL |
|---|---|---|
src/pages/users/[id].js | /users/:id | /users/42 |
src/pages/blog/[slug].js | /blog/:slug | /blog/hello-world |
src/pages/users/[id]/posts/[pid].js | /users/:id/posts/:pid | /users/42/posts/123 |
Nested Routes
| File Path | Generated Route | Notes |
|---|---|---|
src/pages/settings/index.js | /settings | Index page |
src/pages/settings/profile.js | /settings/profile | Sub-page |
src/pages/settings/security.js | /settings/security | Sub-page |
src/pages/settings/[section].js | /settings/:section | Dynamic section |
🎯 Advanced Examples
Blog with Posts
// src/pages/blog/index.js - Lists all posts
export default () => {
const posts = $([]);
$.effect(() => {
fetch('/api/posts')
.then(res => res.json())
.then(data => posts(data));
});
return html`
<div>
<h1>Blog</h1>
${posts().map(post => html`
<article>
<h2><a href="#/blog/${post.slug}">${post.title}</a></h2>
<p>${post.excerpt}</p>
</article>
`)}
</div>
`;
};// src/pages/blog/[slug].js - Single post
export default (params) => {
const post = $(null);
const slug = params.slug;
$.effect(() => {
fetch(`/api/posts/${slug}`)
.then(res => res.json())
.then(data => post(data));
});
return html`
<div>
<a href="#/blog">← Back to blog</a>
${() => post() ? html`
<article>
<h1>${post().title}</h1>
<div>${post().content}</div>
</article>
` : html`<div>Loading...</div>`}
</div>
`;
};Dashboard with Nested Sections
// src/pages/dashboard/index.js
export default () => {
return html`
<div class="dashboard">
<nav>
<a href="#/dashboard">Overview</a>
<a href="#/dashboard/analytics">Analytics</a>
<a href="#/dashboard/settings">Settings</a>
</nav>
<main>
<h1>Dashboard Overview</h1>
<!-- Overview content -->
</main>
</div>
`;
};// src/pages/dashboard/analytics.js
export default () => {
return html`
<div class="dashboard">
<nav>
<a href="#/dashboard">Overview</a>
<a href="#/dashboard/analytics">Analytics</a>
<a href="#/dashboard/settings">Settings</a>
</nav>
<main>
<h1>Analytics</h1>
<!-- Analytics content -->
</main>
</div>
`;
};E-commerce Product Routes
// src/pages/products/[category]/[id].js
export default (params) => {
const { category, id } = params;
const product = $(null);
$.effect(() => {
fetch(`/api/products/${category}/${id}`)
.then(res => res.json())
.then(data => product(data));
});
return html`
<div class="product-page">
<nav class="breadcrumbs">
<a href="#/products">Products</a> >
<a href="#/products/${category}">${category}</a> >
<span>${id}</span>
</nav>
${() => product() ? html`
<div class="product">
<h1>${product().name}</h1>
<p class="price">$${product().price}</p>
<p>${product().description}</p>
<button @click=${() => addToCart(product())}>
Add to Cart
</button>
</div>
` : html`<div>Loading...</div>`}
</div>
`;
};🔧 Configuration Options
The plugin accepts an optional configuration object:
// vite.config.js
import { defineConfig } from 'vite';
import { sigproRouter } from 'sigpro/vite';
export default defineConfig({
plugins: [
sigproRouter({
pagesDir: 'src/pages', // Default: 'src/pages'
extensions: ['.js', '.jsx'], // Default: ['.js', '.jsx']
exclude: ['**/_*', '**/components/**'] // Glob patterns to exclude
})
]
});Options
| Option | Type | Default | Description |
|---|---|---|---|
pagesDir | string | 'src/pages' | Directory containing your pages |
extensions | string[] | ['.js', '.jsx'] | File extensions to include |
exclude | string[] | [] | Glob patterns to exclude |
🎯 Route Priority
The plugin automatically sorts routes to ensure correct matching:
- Static routes take precedence over dynamic ones
- More specific routes (deeper paths) come first
- Alphabetical order for routes at the same level
Example sorting:
/users/new (static, specific)
/users/[id]/edit (dynamic, deeper)
/users/[id] (dynamic, shallower)
/users/profile (static, shallower)📦 Output Example
When you import virtual:sigpro-routes, you get:
// Generated module
import Page_0 from '/src/pages/index.js';
import Page_1 from '/src/pages/about.js';
import Page_2 from '/src/pages/blog/index.js';
import Page_3 from '/src/pages/blog/[slug].js';
import Page_4 from '/src/pages/users/[id].js';
import Page_5 from '/src/pages/users/[id]/edit.js';
export const routes = [
{ path: '/', component: Page_0 },
{ path: '/about', component: Page_1 },
{ path: '/blog', component: Page_2 },
{ path: '/blog/:slug', component: Page_3 },
{ path: '/users/:id', component: Page_4 },
{ path: '/users/:id/edit', component: Page_5 },
];🚀 Performance Benefits
- Automatic code splitting - Each page becomes a separate chunk
- Lazy loading ready - Import pages dynamically
- Tree shaking - Only used routes are included
// With dynamic imports (automatic with Vite)
const routes = [
{ path: '/', component: () => import('./pages/index.js') },
{ path: '/about', component: () => import('./pages/about.js') },
// ...
];💡 Pro Tips
1. Group Related Pages
src/pages/
├── dashboard/
│ ├── index.js
│ ├── analytics.js
│ └── settings.js
└── dashboard.js # ❌ Don't mix with folder2. Use Index Files for Clean URLs
✅ Good:
pages/blog/index.js → /blog
pages/blog/post.js → /blog/post
❌ Avoid:
pages/blog.js → /blog (conflicts with folder)3. Private Components
Prefix with underscore to exclude from routing:
src/pages/
├── index.js
├── about.js
└── _components/ # ❌ Not scanned
└── Header.js4. Layout Components
Create a layout wrapper in your main entry:
// main.js
import { $, html } from 'sigpro';
import { routes } from 'virtual:sigpro-routes';
// Wrap all routes with layout
const routesWithLayout = routes.map(route => ({
...route,
component: (params) => Layout(route.component(params))
}));
const router = $.router(routesWithLayout);
document.body.appendChild(router);Note: This plugin is completely optional. You can always define routes manually if you prefer. The plugin just saves you from writing boilerplate route configurations.
Pro Tip: The plugin works great with hot module replacement (HMR) - add a new page and it's instantly available in your dev server without restarting!