Extending
Adding Routing Options

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:

Routing Ecosystem: Your routing integration expands React Kickstart's navigation capabilities and provides developers with modern, flexible routing options.