When building Next.js apps, most developers write route paths directly in their code (means they hard-code the route path). This seems easy and fast at first, but it creates big problems when your app grows bigger.
The Problem with Hardcoded Routes
Consider this common pattern you've probably seen (or written) before:
1// ❌ Problematic approach
2
3import Link from 'next/link'
4import { useRouter } from 'next/router'
5
6export default function Navbar() {
7 const router = useRouter()
8
9 const handleNavigation = () => {
10 router.push('/dashboard/user/profile')
11 }
12
13 return (
14 <nav>
15 <Link href="/about">About</Link>
16 <Link href="/contact">Contact</Link>
17 <Link href="/blog">Blog</Link>
18 <Link href="/dashboard/analytics">Analytics</Link>
19 <button onClick={handleNavigation}>Profile</button>
20 </nav>
21 )
22}In above code snippet all the route path are hard-coded.
Why This Approach Fails
1. Maintenance Issue
When you need to change a route from /blog to /articles, you'll have to hunt through your entire codebase to find and update every occurrence.
2. Chances of Typos
It's easy to mistype /dashbord instead of /dashboard, leading to broken links that might not be caught until production.
3. No IntelliSense Support Your IDE can't help you with autocomplete or catch errors when routes are just strings scattered throughout your code.
4. Team Collaboration Issues Different team members might use slightly different paths for the same route, creating inconsistencies.
5. Testing Difficulties Hardcoded routes make it harder to write comprehensive tests and mock navigation behavior.
The Solution: Centralized Route Management
The solution is to create a centralized route configuration that serves as the single source of truth for all your application's routes.
Let me show you how with step by step process.
Step 1: Create Your Routes Configuration
First, create a dedicated file for your routes. I recommend placing it in a constants or config directory:
1/src
2 /app
3 /components
4 /constants
5 └── routes.ts
6 /libStep 2: Define Your Routes Object
1// constants/routes.ts
2export const ROUTES = {
3 // Public routes
4 HOME: '/',
5 ABOUT: '/about',
6 CONTACT: '/contact',
7 PRICING: '/pricing',
8
9 // Auth routes
10 LOGIN: '/auth/login',
11 REGISTER: '/auth/register',
12 FORGOT_PASSWORD: '/auth/forgot-password',
13
14 // Blog routes
15 BLOG: '/blog',
16 BLOG_DETAIL: (slug: string) => `/blog/${slug}`,
17 BLOG_CATEGORY: (category: string) => `/blog/category/${category}`,
18
19 // Dashboard routes
20 DASHBOARD: '/dashboard',
21 DASHBOARD_ANALYTICS: '/dashboard/analytics',
22 DASHBOARD_SETTINGS: '/dashboard/settings',
23 DASHBOARD_PROFILE: '/dashboard/profile',
24
25 // Dynamic user routes
26 USER_PROFILE: (userId: string) => `/user/${userId}`,
27 USER_POSTS: (userId: string) => `/user/${userId}/posts`,
28
29 // API routes (useful for fetch calls)
30 API: {
31 USERS: '/api/users',
32 POSTS: '/api/posts',
33 AUTH: '/api/auth',
34 },
35} as const
36
37// Export route groups for better organization
38export const AUTH_ROUTES = {
39 LOGIN: ROUTES.LOGIN,
40 REGISTER: ROUTES.REGISTER,
41 FORGOT_PASSWORD: ROUTES.FORGOT_PASSWORD,
42} as const
43
44export const DASHBOARD_ROUTES = {
45 HOME: ROUTES.DASHBOARD,
46 ANALYTICS: ROUTES.DASHBOARD_ANALYTICS,
47 SETTINGS: ROUTES.DASHBOARD_SETTINGS,
48 PROFILE: ROUTES.DASHBOARD_PROFILE,
49} as constStep 3: Use Routes in Your Components
Now your components become much cleaner and more maintainable:
1// components/Navbar.tsx
2import Link from 'next/link'
3import { useRouter } from 'next/router'
4import { ROUTES, DASHBOARD_ROUTES } from '@/constants/routes'
5
6export default function Navbar() {
7 const router = useRouter()
8
9 const handleProfileNavigation = () => {
10 router.push(DASHBOARD_ROUTES.PROFILE)
11 }
12
13 return (
14 <nav className="flex space-x-4">
15 <Link href={ROUTES.ABOUT} className="hover:text-blue-500">
16 About
17 </Link>
18 <Link href={ROUTES.CONTACT} className="hover:text-blue-500">
19 Contact
20 </Link>
21 <Link href={ROUTES.BLOG} className="hover:text-blue-500">
22 Blog
23 </Link>
24 <Link href={DASHBOARD_ROUTES.ANALYTICS} className="hover:text-blue-500">
25 Analytics
26 </Link>
27 <button
28 onClick={handleProfileNavigation}
29 className="rounded bg-blue-500 px-4 py-2 text-white"
30 >
31 Profile
32 </button>
33 </nav>
34 )
35}Step 4: Handle Dynamic Routes Elegantly
Dynamic routes become much more readable and type-safe:
1// components/BlogList.tsx
2import Link from 'next/link'
3import { ROUTES } from '@/constants/routes'
4
5interface Post {
6 slug: string
7 title: string
8 category: string
9}
10
11interface BlogListProps {
12 posts: Post[]
13}
14
15export default function BlogList({ posts }: BlogListProps) {
16 return (
17 <div className="space-y-4">
18 {posts.map((post) => (
19 <article key={post.slug} className="rounded border p-4">
20 <h2 className="text-xl font-bold">
21 <Link href={ROUTES.BLOG_DETAIL(post.slug)} className="hover:text-blue-500">
22 {post.title}
23 </Link>
24 </h2>
25 <Link
26 href={ROUTES.BLOG_CATEGORY(post.category)}
27 className="text-sm text-gray-500 hover:text-blue-500"
28 >
29 Category: {post.category}
30 </Link>
31 </article>
32 ))}
33 </div>
34 )
35}Benefits of This Approach
-
No Repetition – Define once, use everywhere.
-
No Typos – Reduce the risk of route misnaming.
-
Easy Refactor – Change the route in one place, and it updates across the app.
-
Scalable – As your app grows, this structure keeps things organized.
-
Improved Development Experience – You get autocomplete and type safety in editors like VSCode.
Centralizing your routes in a routes.ts file is a small habit that pays off big in the long run. It keeps your code DRY, readable, and easier to maintain, especially in teams and large-scale projects.
Start doing this early in your Next.js projects, and you’ll thank yourself later.
Written by
Abdul Basit
Frontend developer passionate about JavaScript, React, and building great web experiences. Writing about web development to help developers level up their skills.
Continue reading
