Adding a New Routing Option
Comprehensive guide to adding new routing solutions for React applications, including React Router variants, alternative routing libraries, and Next.js routing enhancements.
Current Support: React Kickstart supports React Router for Vite and Next.js App/Pages Router. This guide shows how to add additional routing options.
๐ฏ Integration Overview
๐ Complete Integration Guide
Declare Routing Dependencies
File: src/builders/dependencies.js
Add your routing library dependencies:
// Reach Router (legacy support)
export const routing = {
// ...existing routing dependencies
reachRouter: "^1.3.4",
reachRouterDom: "^1.3.4"
}
export function getReachRouterDependencies() {
return {
"@reach/router": routing.reachRouter,
"@reach/router-dom": routing.reachRouterDom
}
}
Create Routing Setup Module
File: src/features/routing/your-routing-setup.js
Create comprehensive routing setup modules:
// src/features/routing/wouter-setup.js
import { CORE_UTILS } from '../../utils/index.js'
import path from 'path'
export class WouterSetup {
async setup(projectPath, userChoices) {
await this.createRouterConfig(projectPath, userChoices)
await this.createRoutes(projectPath, userChoices)
await this.createPages(projectPath, userChoices)
await this.createNavigation(projectPath, userChoices)
}
async createRouterConfig(projectPath, userChoices) {
const { typescript } = userChoices
const routerContent = `
import { Route, Switch, Link } from 'wouter'
${typescript ? "import type { ComponentType } from 'react'" : ''}
// Import pages
import Home from '../pages/Home'
import About from '../pages/About'
import NotFound from '../pages/NotFound'
${typescript ? `
interface RouteConfig {
path: string
component: ComponentType
exact?: boolean
}
const routes: RouteConfig[] = [
{ path: '/', component: Home, exact: true },
{ path: '/about', component: About },
]
` : `
const routes = [
{ path: '/', component: Home, exact: true },
{ path: '/about', component: About },
]
`}
export function AppRouter() {
return (
<Switch>
{routes.map(({ path, component: Component }) => (
<Route key={path} path={path} component={Component} />
))}
<Route component={NotFound} />
</Switch>
)
}
export { Link }
`
const routerPath = path.join(projectPath, 'src', 'router', `index.${typescript ? 'tsx' : 'jsx'}`)
await CORE_UTILS.ensureDirectory(path.dirname(routerPath))
await CORE_UTILS.writeFile(routerPath, routerContent.trim())
}
async createPages(projectPath, userChoices) {
const { typescript } = userChoices
// Home page
const homeContent = `
import React from 'react'
import { Link } from 'wouter'
${typescript ? 'const Home: React.FC = () => {' : 'function Home() {'}
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold mb-4">Welcome Home</h1>
<p className="text-lg text-gray-600 mb-6">
This is the home page of your React Kickstart application with Wouter routing.
</p>
<div className="space-x-4">
<Link href="/about">
<a className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
About Us
</a>
</Link>
</div>
</div>
)
}
export default Home
`
const homePath = path.join(projectPath, 'src', 'pages', `Home.${typescript ? 'tsx' : 'jsx'}`)
await CORE_UTILS.ensureDirectory(path.dirname(homePath))
await CORE_UTILS.writeFile(homePath, homeContent.trim())
// About page
const aboutContent = `
import React from 'react'
import { Link } from 'wouter'
${typescript ? 'const About: React.FC = () => {' : 'function About() {'}
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold mb-4">About Us</h1>
<p className="text-lg text-gray-600 mb-6">
Learn more about this React Kickstart application built with Wouter.
</p>
<div className="space-x-4">
<Link href="/">
<a className="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600">
Back Home
</a>
</Link>
</div>
</div>
)
}
export default About
`
const aboutPath = path.join(projectPath, 'src', 'pages', `About.${typescript ? 'tsx' : 'jsx'}`)
await CORE_UTILS.writeFile(aboutPath, aboutContent.trim())
// 404 page
const notFoundContent = `
import React from 'react'
import { Link } from 'wouter'
${typescript ? 'const NotFound: React.FC = () => {' : 'function NotFound() {'}
return (
<div className="container mx-auto px-4 py-8 text-center">
<h1 className="text-6xl font-bold text-gray-400 mb-4">404</h1>
<h2 className="text-2xl font-semibold mb-4">Page Not Found</h2>
<p className="text-gray-600 mb-6">
The page you're looking for doesn't exist.
</p>
<Link href="/">
<a className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
Go Home
</a>
</Link>
</div>
)
}
export default NotFound
`
const notFoundPath = path.join(projectPath, 'src', 'pages', `NotFound.${typescript ? 'tsx' : 'jsx'}`)
await CORE_UTILS.writeFile(notFoundPath, notFoundContent.trim())
}
async createNavigation(projectPath, userChoices) {
const { typescript } = userChoices
const navContent = `
import React from 'react'
import { Link, useLocation } from 'wouter'
${typescript ? `
interface NavItem {
path: string
label: string
}
const navItems: NavItem[] = [
{ path: '/', label: 'Home' },
{ path: '/about', label: 'About' },
]
` : `
const navItems = [
{ path: '/', label: 'Home' },
{ path: '/about', label: 'About' },
]
`}
${typescript ? 'const Navigation: React.FC = () => {' : 'function Navigation() {'}
const [location] = useLocation()
return (
<nav className="bg-white shadow-sm border-b">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
<div className="flex items-center space-x-8">
<Link href="/">
<a className="text-xl font-bold text-gray-900">
React Kickstart
</a>
</Link>
<div className="flex space-x-4">
{navItems.map(({ path, label }) => (
<Link key={path} href={path}>
<a className={\`px-3 py-2 rounded-md text-sm font-medium transition-colors \${
location === path
? 'bg-blue-100 text-blue-700'
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-50'
}\`}>
{label}
</a>
</Link>
))}
</div>
</div>
</div>
</div>
</nav>
)
}
export default Navigation
`
const navPath = path.join(projectPath, 'src', 'components', `Navigation.${typescript ? 'tsx' : 'jsx'}`)
await CORE_UTILS.ensureDirectory(path.dirname(navPath))
await CORE_UTILS.writeFile(navPath, navContent.trim())
}
}
Update Prompt System
File: src/prompts/steps/routing-step.js
Add your routing options to the choices:
getChoices(previousAnswers) {
const { framework } = previousAnswers
// Base routing options
const choices = [
{
name: 'React Router (Standard client-side routing)',
value: 'react-router',
short: 'React Router'
},
{
name: 'Wouter (Minimalist routing library)',
value: 'wouter',
short: 'Wouter'
},
{
name: 'TanStack Router (Type-safe routing)',
value: 'tanstack-router',
short: 'TanStack Router'
},
{
name: 'Hash Router (URL hash-based routing)',
value: 'hash-router',
short: 'Hash Router'
},
{
name: 'None (Single page application)',
value: 'none',
short: 'None'
}
]
// Filter choices based on framework
if (framework === 'nextjs') {
// Next.js has its own routing, so skip this step
return []
}
return choices
}
shouldSkip(previousAnswers) {
// Skip routing step for Next.js (it has built-in routing)
return previousAnswers.framework === 'nextjs'
}
getRecommendation(previousAnswers) {
const { typescript } = previousAnswers
if (typescript) {
return {
choice: 'tanstack-router',
reason: 'TanStack Router provides excellent TypeScript support and type safety'
}
}
return {
choice: 'react-router',
reason: 'React Router is the most popular and well-supported routing solution'
}
}
Integrate with Feature System
File: src/features/routing/index.js
Update the main routing integration:
import { ReactRouterSetup } from "./react-router-setup.js";
import { WouterSetup } from "./wouter-setup.js";
import { TanStackRouterSetup } from "./tanstack-router-setup.js";
import { HashRouterSetup } from "./hash-router-setup.js";
export async function setupRouting(projectPath, userChoices) {
const { routing, framework } = userChoices;
// Skip routing setup for Next.js (has built-in routing)
if (framework === "nextjs") {
return;
}
switch (routing) {
case "react-router":
const reactRouterSetup = new ReactRouterSetup();
await reactRouterSetup.setup(projectPath, userChoices);
break;
case "wouter":
const wouterSetup = new WouterSetup();
await wouterSetup.setup(projectPath, userChoices);
break;
case "tanstack-router":
const tanstackSetup = new TanStackRouterSetup();
await tanstackSetup.setup(projectPath, userChoices);
break;
case "hash-router":
const hashRouterSetup = new HashRouterSetup();
await hashRouterSetup.setup(projectPath, userChoices);
break;
case "none":
default:
// No routing setup
break;
}
}
export function getRoutingInfo(userChoices) {
const { routing } = userChoices;
const routingInfo = {
"react-router": {
description: "Declarative routing for React applications",
features: [
"Nested routing",
"Code splitting",
"Route guards",
"History management",
],
bundleSize: "~13kb",
},
wouter: {
description: "Minimalist routing library with hooks",
features: [
"Lightweight",
"Hook-based",
"Pattern matching",
"SSR support",
],
bundleSize: "~2kb",
},
"tanstack-router": {
description: "Type-safe router with powerful features",
features: ["Type safety", "Search params", "Loaders", "DevTools"],
bundleSize: "~15kb",
},
"hash-router": {
description: "Hash-based routing for static hosting",
features: [
"Static hosting friendly",
"No server config",
"Simple deployment",
],
bundleSize: "~13kb",
},
};
return routingInfo[routing] || null;
}
Update Template System
File: src/templates/frameworks/vite/content-generator.js
Update templates to integrate routing:
generateAppComponent(userChoices) {
const { routing, typescript } = userChoices
let imports = [`import React from 'react'`]
let appContent = ''
switch (routing) {
case 'react-router':
imports.push(`import { BrowserRouter } from 'react-router-dom'`)
imports.push(`import { AppRouter } from './router'`)
appContent = `
<BrowserRouter>
<AppRouter />
</BrowserRouter>
`
break
case 'wouter':
imports.push(`import { AppRouter } from './router'`)
appContent = `<AppRouter />`
break
case 'tanstack-router':
imports.push(`import { AppRouter } from './router'`)
appContent = `<AppRouter />`
break
case 'hash-router':
imports.push(`import AppRouter from './router'`)
appContent = `<AppRouter />`
break
default:
// No routing - single page app
appContent = `
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
Welcome to React Kickstart!
</h1>
<p className="text-lg text-gray-600">
Your single page React application is ready to go.
</p>
</div>
</div>
`
}
return `${imports.join('\n')}
function App() {
return (
${appContent}
)
}
export default App`
}
Add to QA System
File: qa-automation/test-matrix-generator.js
Add routing options to the test matrix:
const CONFIG_OPTIONS = {
framework: ["vite", "nextjs"],
routing: ["react-router", "wouter", "tanstack-router", "hash-router", "none"],
typescript: [true, false],
// ... other options
};
// Add routing-specific validation
const ROUTING_VALIDATION = {
"react-router": {
requiredFiles: ["src/router/index.tsx", "src/pages/Home.tsx"],
requiredDependencies: ["react-router-dom"],
},
wouter: {
requiredFiles: ["src/router/index.tsx", "src/pages/Home.tsx"],
requiredDependencies: ["wouter"],
},
"tanstack-router": {
requiredFiles: ["src/router/index.tsx", "src/router/routeTree.ts"],
requiredDependencies: ["@tanstack/react-router"],
},
"hash-router": {
requiredFiles: ["src/router/index.tsx", "src/pages/Home.tsx"],
requiredDependencies: ["react-router-dom"],
},
};
// Framework-specific routing validation
function validateRouting(projectPath, userChoices) {
const { framework, routing } = userChoices;
// Next.js should not have client-side routing setup
if (framework === "nextjs" && routing && routing !== "none") {
throw new Error(
"Next.js projects should not have client-side routing configured"
);
}
// Vite projects should have routing setup if specified
if (framework === "vite" && routing && routing !== "none") {
const validation = ROUTING_VALIDATION[routing];
if (validation) {
// Validate required files exist
for (const file of validation.requiredFiles) {
const filePath = path.join(projectPath, file);
if (!fs.existsSync(filePath)) {
throw new Error(`Missing routing file: ${file}`);
}
}
}
}
}
๐งช Testing Your Integration
Manual Testing
Test your routing integration:
# Test Wouter routing
node bin/react-kickstart.js test-wouter --yes --framework vite --routing wouter --typescript
# Test TanStack Router
node bin/react-kickstart.js test-tanstack --yes --framework vite --routing tanstack-router
# Test Hash Router
node bin/react-kickstart.js test-hash --yes --framework vite --routing hash-router
Verify Routing Works
cd test-wouter
npm install
npm run dev # Should start with working navigation
# Test navigation between routes
QA Automation
# Test routing combinations
node qa-automation/test-runner.js critical --routing wouter,tanstack-router
๐ Best Practices
Routing Strategy: Choose routing solutions that match the application's complexity and deployment requirements.
- Route Structure - Organize routes logically with clear hierarchies
- Code Splitting - Implement lazy loading for better performance
- Type Safety - Provide TypeScript support for route parameters and navigation
- SEO Considerations - Consider SEO implications for different routing approaches
- Error Handling - Include proper 404 and error boundary handling
๐ Next Steps
Related Guides:
- Adding Features โ - General feature addition guide
- Architecture Overview โ - Understanding the system design
- Contributing โ - Contribution guidelines and workflow
Routing Ecosystem: Your routing integration expands React Kickstart's navigation capabilities and provides developers with modern, flexible routing options.