@piccolojnr/next-typed-routes
v1.1.2
Published
Type-safe route generation and navigation for Next.js App Router
Maintainers
Readme
Next Typed Routes
Type-safe route generation and navigation for Next.js App Router with a clean, non-polluting approach.
✨ Features
- 🔍 Automatic Route Discovery: Scans your Next.js App Router structure
- 🎯 Type-Safe Navigation: Generate TypeScript types for all your routes
- 🛠️ Runtime Utilities: Type-safe route generation and validation
- ⚡ Zero Configuration: Works out of the box with Next.js App Router
- 🔄 Watch Mode: Automatically regenerate types when files change
- ⚛️ React Components: Type-safe Link and router components
- 🧹 Clean Output: No pollution of your
src/directory
📦 Installation
npm install @piccolojnr/next-typed-routes🚀 Quick Start
Generate route types:
npx @piccolojnr/next-typed-routesUse in your code:
// Import everything from the clean barrel file import { allRoutes, AppRoute, isValidRoute, route } from "@/typed-routes"; // Type-safe route usage const userUrl = route("/user/[id]", { params: { id: "123" } }); const isValid = isValidRoute("/user/123"); // true
📁 Clean Directory Structure
The generator creates a clean, organized structure in your project:
your-project/
├── typed-routes/ # Clean, dedicated directory
│ ├── generated/
│ │ └── routes.ts # Generated types and constants
│ ├── route.ts # Route utilities and types
│ └── react.tsx # React components
├── src/
│ └── app/ # Your Next.js pages (untouched!)
└── package.json🎯 Key Benefits
- ✅ No
src/pollution: Generated files stay in their own directory - ✅ Version control friendly: Easy to
.gitignoreor commit as needed - ✅ Tree-shakeable: Import only what you need
- ✅ Type-safe: Full TypeScript support throughout
- ✅ Modular structure: Separate files for different concerns
📖 Usage Guide
Basic Route Generation
import { AppRoute, isValidRoute, route } from "@/typed-routes";
// Static routes
const aboutUrl = route("/about"); // "/about"
// Dynamic routes with parameters
const userUrl = route("/user/[id]", {
params: { id: "42" },
search: { sort: "asc" },
}); // "/user/42?sort=asc"
// Nested dynamic routes
const postUrl = route("/user/[id]/posts/[postId]", {
params: { id: "42", postId: "abc" },
}); // "/user/42/posts/abc"
// Runtime validation
const isValid = isValidRoute("/user/123"); // true
const isInvalid = isValidRoute("/invalid/route"); // falseWorking with All Routes
import { allRoutes, getAllRoutes } from "@/typed-routes";
// Get all routes as a constant
console.log(allRoutes); // readonly array of all routes
// Get all routes as a function
const routes = getAllRoutes();
routes.forEach((route) => console.log(route));Type-Safe Route Usage
import type { AppRoute, TypedRoute } from "@/typed-routes";
// Use AppRoute type for type safety
const validRoutes: AppRoute[] = [
"/",
"/about",
"/user/[id]",
"/user/[id]/posts/[postId]",
];
// TypedRoute is an alias for AppRoute
const route: TypedRoute = "/user/[id]";⚛️ React Components
Typed Link Component
import Link from "next/link";
import { createTypedLink } from "@/typed-routes/react";
const TypedLink = createTypedLink(Link);
function Navigation() {
return (
<nav>
{/* Static routes */}
<TypedLink href="/about">About</TypedLink>
{/* Dynamic routes with parameters */}
<TypedLink
href="/user/[id]"
params={{ id: "123" }}
>
User Profile
</TypedLink>
{/* Dynamic routes with search parameters */}
<TypedLink
href="/user/[id]/posts/[postId]"
params={{ id: "123", postId: "abc" }}
search={{ sort: "asc", filter: "recent" }}
>
User Post
</TypedLink>
</nav>
);
}Typed Router Hook
import { useRouter } from "next/navigation";
import { createTypedRouter } from "@/typed-routes/react";
const useTypedRouter = createTypedRouter(useRouter);
function MyComponent() {
const router = useTypedRouter();
const handleNavigation = () => {
// Static route navigation
router.push("/about");
// Dynamic route navigation
router.push("/user/[id]", { params: { id: "123" } });
// Dynamic route with search params
router.push("/user/[id]/posts/[postId]", {
params: { id: "123", postId: "abc" },
search: { sort: "desc" },
});
// Replace current route
router.replace("/user/[id]", { params: { id: "456" } });
// Prefetch route
router.prefetch("/user/[id]", { params: { id: "123" } });
};
return <button onClick={handleNavigation}>Navigate</button>;
}🛠️ CLI Usage
# Generate once with defaults
npx @piccolojnr/next-typed-routes
# Watch mode for development
npx @piccolojnr/next-typed-routes --watch
# Custom pages directory
npx @piccolojnr/next-typed-routes --pages-dir app
# Custom output location
npx @piccolojnr/next-typed-routes --output custom/routes.ts
# Add route prefix
npx @piccolojnr/next-typed-routes --prefix /api
# Include route groups
npx @piccolojnr/next-typed-routes --include-route-groupsCLI Options
| Option | Alias | Description | Default |
| ------------------------ | ----- | --------------------------------------------------- | ---------------------------------- |
| --watch | -w | Watch for file changes and regenerate automatically | false |
| --pages-dir | -p | Custom pages directory path | src/app |
| --output | -o | Custom output file path | typed-routes/generated/routes.ts |
| --prefix | | Custom route prefix | "" |
| --include-route-groups | | Include route groups in the output | false |
| --help | -h | Show help message | |
⚙️ Configuration
The generator automatically detects your Next.js App Router structure and generates TypeScript definitions.
Default Settings
- Pages Directory:
src/app - Output Directory:
typed-routes/ - Generated File:
typed-routes/generated/routes.ts - Route Utilities:
typed-routes/route.ts - React Components:
typed-routes/react.tsx - File Extensions:
page.tsx,page.ts,page.jsx,page.js - Route Groups: Excluded by default
File Structure Detection
The generator scans for page files following Next.js App Router conventions:
src/app/
├── page.tsx → "/"
├── about/
│ └── page.tsx → "/about"
├── user/
│ └── [id]/
│ ├── page.tsx → "/user/[id]"
│ └── posts/
│ └── [postId]/
│ └── page.tsx → "/user/[id]/posts/[postId]"
└── (marketing)/ # Route groups (excluded by default)
└── pricing/
└── page.tsx → "/pricing"📚 Generated Files
typed-routes/generated/routes.ts
Contains the core types and constants:
// Generated route types
export type AppRoute =
| "/"
| "/about"
| "/user/[id]"
| "/user/[id]/posts/[postId]";
// All routes as a constant array
export const allRoutes: readonly AppRoute[] = [
"/",
"/about",
"/user/[id]",
"/user/[id]/posts/[postId]",
] as const;
// Type alias for better DX
export type TypedRoute = AppRoute;typed-routes/route.ts
Contains route utilities and type definitions:
// Route utility function
export function route<T extends AppRoute>(
template: T,
options?: {
params?: ParamRecord<T>;
search?: SearchParams;
}
): string;
// Runtime validation
export function isValidRoute(path: string): boolean;
// Get all routes
export function getAllRoutes(): readonly string[];
// Type definitions
export type ExtractParams<T extends string>;
export type ParamRecord<T extends string>;
export type SearchParams;
export type RoutesWithParams;
export type RoutesWithoutParams;typed-routes/react.tsx
Contains React component utilities:
// Create typed Link component
export function createTypedLink<CompProps extends LinkProps>(
Component: React.ComponentType<CompProps>,
);
// Create typed router hook
export function createTypedRouter(useRouter?: typeof nextUseRouter);
// Pre-configured hook
export const useTypedRouter;🎯 Version Control
You have two options for version control:
Option 1: Commit Generated Files (Recommended)
# Don't ignore typed-routes - commit them for team consistency
# typed-routes/Benefits:
- Team members get consistent types without running generation
- CI/CD doesn't need to run generation step
- Easier debugging and code reviews
Option 2: Ignore Generated Files
# Ignore generated files
typed-routes/Benefits:
- Smaller repository size
- Forces developers to run generation locally
- No merge conflicts in generated files
Requirements:
- Add generation to your build process
- Ensure all team members run generation locally
🔧 Integration Examples
Next.js Project Setup
Install and generate:
npm install @piccolojnr/next-typed-routes npx @piccolojnr/next-typed-routesAdd to package.json scripts:
{ "scripts": { "dev": "next dev", "build": "@piccolojnr/next-typed-routes && next build", "routes": "@piccolojnr/next-typed-routes", "routes:watch": "@piccolojnr/next-typed-routes --watch" } }TypeScript path mapping (optional):
// tsconfig.json { "compilerOptions": { "paths": { "@/typed-routes": ["./typed-routes"] } } }
Development Workflow
# Start development with route watching
npm run routes:watch &
npm run dev
# Or generate once
npm run routes
npm run dev📋 Examples
See the examples/ directory for complete usage examples:
examples/usage-example.ts- Basic usage patternsexamples/react-usage.tsx- React component examples
🤝 Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
📄 License
MIT License - see the LICENSE file for details.
Next Typed Routes - Clean, type-safe routing for Next.js App Router 🚀
