rsbuild-plugin-pages
v0.0.3
Published
Rsbuild plugin for filesystem routing and Express API
Maintainers
Readme
rsbuild-plugin-pages
A powerful Rsbuild plugin that enables filesystem-based routing similar to Next.js, with full support for Express.js@5 API routes.
Features
- 🚀 Filesystem-based routing - Automatic route generation from your file structure
- 🔥 Next.js-style conventions - Familiar routing patterns with
[id]and[...params]support - ⚡ Express.js@5 API routes - Full-featured API endpoints with HTTP method support
- 📝 TypeScript first - Complete type safety for routes and parameters
- 🔄 Hot reloading - Automatic route updates during development
- 📦 Code splitting - Dynamic imports for optimal bundle size
- 🛠️ Zero config - Works out of the box with sensible defaults
Installation
npm install rsbuild-plugin-pages
# or
yarn add rsbuild-plugin-pages
# or
pnpm add rsbuild-plugin-pagesQuick Start
1. Add the plugin to your Rsbuild config
// rsbuild.config.ts
import { defineConfig } from '@rsbuild/core';
import { pluginPages } from 'rsbuild-plugin-pages';
export default defineConfig({
plugins: [
// pluginPages returns an array of plugins (virtual module + pages plugin)
...pluginPages({
pagesDir: 'src/pages',
apiDir: 'src/pages/api',
}),
],
});2. Create your page structure
src/
pages/
index.tsx # → /
about.tsx # → /about
blog/
index.tsx # → /blog
[slug].tsx # → /blog/:slug
users/
[id].tsx # → /users/:id
[...params].tsx # → /users/*params
api/
hello.ts # → /api/hello
users/
[id].ts # → /api/users/:id3. Create your pages
// src/pages/index.tsx
export default function HomePage() {
return <h1>Welcome to the home page!</h1>;
}
// src/pages/blog/[slug].tsx
import { useParams } from 'react-router-dom';
export default function BlogPost() {
const { slug } = useParams();
return <h1>Blog post: {slug}</h1>;
}4. Create API routes
// src/pages/api/hello.ts
import type { Request, Response } from 'express';
export function GET(req: Request, res: Response) {
res.json({ message: 'Hello from API!' });
}
export function POST(req: Request, res: Response) {
res.json({ message: 'Posted!', body: req.body });
}
// src/pages/api/users/[id].ts
import type { Request, Response } from 'express';
export function GET(req: Request, res: Response) {
const { id } = req.params;
res.json({ user: { id, name: `User ${id}` } });
}5. Use the generated routes
// src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import routes from 'virtual:rsbuild-pages/routes';
function App() {
return (
<BrowserRouter>
<Routes>
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
element={<route.component />}
exact={route.exact}
/>
))}
</Routes>
</BrowserRouter>
);
}
export default App;Configuration
Plugin Options
interface PagesPluginOptions {
/**
* Directory containing pages (default: 'src/pages')
*/
pagesDir?: string;
/**
* Directory containing API routes (default: 'src/pages/api')
*/
apiDir?: string;
/**
* File extensions to include (default: ['.tsx', '.ts', '.jsx', '.js'])
*/
extensions?: string[];
/**
* Patterns to ignore
*/
ignore?: string[];
/**
* Enable dynamic imports for code splitting (default: true)
*/
dynamicImports?: boolean;
/**
* Express.js configuration
*/
express?: {
middleware?: ExpressMiddleware[];
};
}Example Configuration
// rsbuild.config.ts
import { defineConfig } from '@rsbuild/core';
import { pluginPages } from 'rsbuild-plugin-pages';
export default defineConfig({
plugins: [
...pluginPages({
pagesDir: 'src/pages',
apiDir: 'src/api',
extensions: ['.tsx', '.ts'],
ignore: ['**/*.test.*', '**/components/**'],
dynamicImports: true,
express: {
middleware: [
{
path: '/api',
handler: (req, res, next) => {
// CORS middleware
res.header('Access-Control-Allow-Origin', '*');
next();
},
},
],
},
}),
],
});Routing Conventions
Page Routes
pages/index.tsx→/pages/about.tsx→/aboutpages/blog/index.tsx→/blogpages/blog/[slug].tsx→/blog/:slugpages/users/[id].tsx→/users/:idpages/posts/[...params].tsx→/posts/*params
API Routes
API routes support all HTTP methods through named exports:
// pages/api/users.ts
import type { Request, Response } from 'express';
export function GET(req: Request, res: Response) {
// Handle GET requests
}
export function POST(req: Request, res: Response) {
// Handle POST requests
}
export function PUT(req: Request, res: Response) {
// Handle PUT requests
}
export function DELETE(req: Request, res: Response) {
// Handle DELETE requests
}Dynamic Routes
Single Parameter
// pages/users/[id].tsx
// Matches: /users/123, /users/abc
import { useParams } from 'react-router-dom';
export default function User() {
const { id } = useParams();
return <div>User ID: {id}</div>;
}Catch-All Routes
// pages/docs/[...params].tsx
// Matches: /docs/a, /docs/a/b, /docs/a/b/c
import { useParams } from 'react-router-dom';
export default function Docs() {
const { params } = useParams();
// params will be 'a/b/c' for /docs/a/b/c
return <div>Docs: {params}</div>;
}Virtual Modules
The plugin creates virtual modules that exist only in memory and are managed by Rsbuild:
virtual:rsbuild-pages/routes- Client-side route definitionsvirtual:rsbuild-pages/api- Express.js router for API routesvirtual:rsbuild-pages/manifest- Complete route manifestvirtual:rsbuild-pages/types- TypeScript definitions
Import Virtual Modules
Import the generated virtual modules directly:
import routes from 'virtual:rsbuild-pages/routes'; // Page routes
import apiRouter from 'virtual:rsbuild-pages/api'; // API router
import manifest from 'virtual:rsbuild-pages/manifest'; // Route manifestTypeScript Support
The plugin generates complete TypeScript definitions for type-safe routing. To use TypeScript with virtual modules, add this to your types.d.ts file:
// types.d.ts
declare module 'virtual:rsbuild-pages/routes' {
import type { PageRoute } from 'rsbuild-plugin-pages';
const routes: PageRoute[];
export default routes;
}
declare module 'virtual:rsbuild-pages/api' {
import type { Router } from 'express';
const router: Router;
export default router;
}
declare module 'virtual:rsbuild-pages/manifest' {
import type { GeneratedRoutes } from 'rsbuild-plugin-pages';
const manifest: GeneratedRoutes;
export default manifest;
}
declare module 'virtual:rsbuild-pages/types' {
// Auto-generated route types
export interface PageRoutes {
'/': [];
'/about': [];
'/blog/:slug': ['slug'];
'/users/:id': ['id'];
'/posts/*params': ['params'];
}
export interface ApiRoutes {
'/hello': [];
'/users/:id': ['id'];
}
// Type-safe route parameters
export type RouteParams<T extends keyof PageRoutes> =
PageRoutes[T] extends string[]
? Record<PageRoutes[T][number], string>
: never;
}Express.js Integration
Basic Setup
// server.ts
import express from 'express';
import apiRouter from 'virtual:rsbuild-pages/api';
const app = express();
// Add JSON parsing
app.use(express.json());
// Add API routes
app.use('/api', apiRouter);
app.listen(3000, () => {
console.log('Server running on port 3000');
});Custom Middleware
// Add custom middleware through plugin options
pluginPages({
express: {
middleware: [
{
path: '/api',
handler: (req, res, next) => {
console.log(`API ${req.method} ${req.path}`);
next();
},
},
],
},
});Advanced Usage
Custom Route Processing
// Access the route manifest for custom processing
import manifest from 'virtual:rsbuild-pages/manifest';
console.log('Available routes:', manifest);
// {
// pages: [...],
// api: [...]
// }Virtual Module Benefits
Using virtual modules provides several advantages:
- 🚀 Performance: No file system I/O - modules exist only in memory
- 🔄 Hot Reloading: Instant updates when routes change
- 🧹 Clean: No generated files cluttering your project
- 🔧 Integration: Seamless integration with Rsbuild's module system
- 📦 Production: Virtual modules are properly bundled for production
Development vs Production
The plugin automatically handles development vs production builds:
- Development: File watching with hot reloading of virtual modules
- Production: Virtual modules are bundled normally by Rsbuild
Examples
Blog Application
src/
pages/
index.tsx # Home page
blog/
index.tsx # Blog listing
[slug].tsx # Individual blog post
category/
[name].tsx # Category page
api/
posts/
index.ts # GET /api/posts
[id].ts # GET /api/posts/:idE-commerce Application
src/
pages/
index.tsx # Home
products/
index.tsx # Product listing
[id].tsx # Product detail
category/
[...path].tsx # Nested categories
api/
products/
index.ts # Product CRUD
[id].ts # Individual product
cart/
index.ts # Cart operationsMigration Guide
From Next.js
The plugin follows Next.js conventions closely, making migration straightforward:
- Move your
pagesdirectory tosrc/pages - Update your router setup to use the generated routes
- API routes work the same way with Express.js instead of Next.js API routes
From React Router
- Remove manual route definitions
- Organize components into the
src/pagesdirectory following the conventions - Import and use the generated routes
Troubleshooting
Common Issues
- Virtual module not found: Make sure you're using the correct import path (
virtual:rsbuild-pages/routes) - Routes not updating: Ensure file watching is working and files are in the correct directory
- TypeScript errors: Add virtual module type declarations (see TypeScript Support section)
- API routes not working: Verify Express.js is properly configured and routes export the correct HTTP methods
- Plugin conflicts: Remember to spread the plugin array:
...pluginPages({...})
Debug Mode
Enable debug logging:
// Set environment variable
process.env.DEBUG = 'rsbuild-pages:*';Development
This project uses Biome for linting and formatting.
Available Scripts
npm run build- Build the plugin for productionnpm run dev- Build in watch mode for developmentnpm test- Run the test suitenpm run lint- Check for linting issuesnpm run lint:fix- Fix linting issues automaticallynpm run format- Check code formattingnpm run format:fix- Fix code formatting automaticallynpm run check- Run both linting and formatting checksnpm run check:fix- Fix both linting and formatting issues
Contributing
We welcome contributions! Please see our Contributing Guide for details.
License
MIT License - see LICENSE for details.
Changelog
See CHANGELOG.md for version history and changes.
