Quick Setup for React Router v6 in Your React App

Passionate and results-driven Full-Stack Developer with more than 4 years of experience in managing the entire application lifecycle, from design to deployment. Proficient in React.js, MERN stack, and microservice architecture, with a track record of building scalable and high-performance applications. A self-motivated technology enthusiast with an entrepreneurial mindset, dedicated to building amazing products that drive business success.
Bootstrapping application
Lets use Vite to setup our react app as it is the go to way of creating a react application as of today. Run the following command in your favorite terminal to get started. Follow the onscreen instructions. I am choosing to use React with TypeScript + SWC here.
npm create vite@latest
Installing react-router-dom
Let's install react-router-dom as our dependancy
npm install react-router-dom
Setup Shadcn/ui
I am using navigation menu from shadcn-ui which is a popular components collection from where you can directly copy-paste into your code. Let's add it via CLI, you can also choose manual. I am using Tailwind for styling here. Here is the official installation guide.
Install Tailwind
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Adding path alias
Add the following code to the tsconfig.json file to resolve paths:
{
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
// ...
}
}
Update vite.config.ts
npm i -D @types/node
Add the following contents to your vite.config.ts file
import path from "path"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})
Initialize Shadcn-ui
npx shadcn-ui@latest init
Add navigation menu
npx shadcn-ui@latest add navigation-menu
Notice the components/ui and lib folders create for you. That's where the navigation-menu is residing. Let's modify a bit. Add the following lines at the bottom of the file.
const ListItem = React.forwardRef<
React.ElementRef<'a'>,
React.ComponentPropsWithoutRef<'a'>
>(({ className, title, children, href = '/', ...props }, ref) => {
return (
<li>
<NavigationMenuLink asChild>
<Link to={href}>
<span
ref={ref}
className={cn(
'block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground',
className
)}
{...props}
>
<div className='text-sm font-medium leading-none'>{title}</div>
<p className='line-clamp-2 text-sm leading-snug text-muted-foreground'>
{children}
</p>
</span>
</Link>
</NavigationMenuLink>
</li>
)
})
export const Navbar = () => {
return (
<NavigationMenu className='relative z-[1] flex w-screen justify-center'>
<NavigationMenuList className='center shadow-blackA4 text-[#7360BF] m-0 flex list-none rounded-[6px] bg-[#181a1b] p-1 shadow-[0_2px_10px]'>
<NavigationMenuItem>
<NavigationMenuTrigger className='text-violet11 hover:bg-violet3 focus:shadow-violet7 group flex select-none items-center justify-between gap-[2px] rounded-[4px] px-3 py-2 text-[15px] font-medium leading-none outline-none focus:shadow-[0_0_0_2px]'>
Getting started
</NavigationMenuTrigger>
<NavigationMenuContent className='bg-[#181a1b] text-white rounded-lg data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight absolute top-0 left-0 w-full sm:w-auto'>
<ul className='p-6 md:w-[400px] lg:w-[500px]'>
{blogs.slice(3).map((blog) => (
<ListItem
key={blog.title}
title={blog.title}
href={`/${blog.id}`}
>
{blog.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger className='text-violet11 hover:bg-violet3 focus:shadow-violet7 group flex select-none items-center justify-between gap-[2px] rounded-[4px] px-3 py-2 text-[15px] font-medium leading-none outline-none focus:shadow-[0_0_0_2px]'>
Components
</NavigationMenuTrigger>
<NavigationMenuContent className='bg-[#181a1b] text-white rounded-lg absolute top-0 left-0 w-full sm:w-auto'>
<ul className='grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] '>
{blogs.map((blog) => (
<ListItem
key={blog.title}
title={blog.title}
href={`/${blog.id}`}
>
{blog.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<a href='/docs'>
<NavigationMenuLink
className={`text-violet11 hover:bg-violet3 focus:shadow-violet7 select-none rounded-[4px] px-3 py-2 text-[15px] font-medium leading-none no-underline outline-none focus:shadow-[0_0_0_2px] flex align-center gap-0.5 ${navigationMenuTriggerStyle()}`}
href='https://github.com/radix-ui'
>
Documentation <ExternalLink size={16} />
</NavigationMenuLink>
</a>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)
}
Pre-routing setup
Let's setup routing in our App.tsx file
Folder structure
We will store all our pages under routes folder instead of pages and lets create our root.tsx route. It is where we are going to setup our layout consuming our navigation menu that we added.
mkdir src/routes
touch src/routes/Root.jsx
// /src/Root.tsx
export const Root = () => {
return (
<div>
<nav className='flex justify-center pt-10'>
<Navbar />
</nav>
<Outlet />
</div>
)
}
Blog Element
Let's create blog which will be the element that we render in our route.
// src/Blog.tsx
import { useParams } from 'react-router-dom'
import { blogs } from '../data'
export const Blog = () => {
const { id: currentId } = useParams()
const blog = blogs.find(({ id }) => currentId === id)
if (!blog) {
throw new Response('', {
status: 404,
statusText: 'Not Found',
})
}
return (
<main className='flex justify-center'>
<section className='mt-10 w-7/12 h-fit bg-[#181a1b] rounded-xl text-white p-5 text-center'>
<h1 className='text-[#7360BF] text-3xl'>{blog.title}</h1>
<p className='p-2 mt-4 text-lg text-left leading-8'>{blog.content}</p>
</section>
</main>
)
}
Stub blog data
Let's add some stub data for blogs
// /src/data/index.ts
const content = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lobortis mattis aliquam faucibus purus in massa tempor nec feugiat. Nec dui nunc mattis enim ut tellus. Nulla at volutpat diam ut. Erat pellentesque adipiscing commodo elit. Fringilla ut morbi tincidunt augue interdum velit euismod in. Eleifend quam adipiscing vitae proin sagittis nisl. Eget nullam non nisi est. Interdum consectetur libero id faucibus nisl tincidunt eget nullam non. Cursus risus at ultrices mi tempus imperdiet. Lectus sit amet est placerat. Sit amet mattis vulputate enim nulla aliquet porttitor lacus luctus. Penatibus et magnis dis parturient montes nascetur. Senectus et netus et malesuada fames ac turpis egestas. Phasellus faucibus scelerisque eleifend donec pretium vulputate sapien nec sagittis. Ullamcorper velit sed ullamcorper morbi tincidunt ornare massa eget egestas. Etiam erat velit scelerisque in. Malesuada proin libero nunc consequat interdum. Purus ut faucibus pulvinar elementum integer enim neque.
Consequat nisl vel pretium lectus quam. Sit amet cursus sit amet dictum. Ornare arcu dui vivamus arcu felis bibendum ut tristique et. Morbi blandit cursus risus at ultrices mi tempus. Elit pellentesque habitant morbi tristique senectus et. Leo duis ut diam quam nulla porttitor. Platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper. Viverra ipsum nunc aliquet bibendum enim facilisis gravida. Scelerisque in dictum non consectetur a erat nam at. Auctor urna nunc id cursus metus aliquam eleifend. Sem integer vitae justo eget magna fermentum.`
export const blogs: {
id: string
title: string
category: string
description: string
content: string
}[] = [
{
id: 'f17c03e0-3f5c-4b61-99b6-efcc347dfe52',
title: 'Understanding JavaScript Closures',
category: 'JavaScript',
description:
'An in-depth look at closures in JavaScript and how to use them effectively.',
content,
},
{
id: '7bcbcad1-c469-419e-a0f8-fabed6cf153b',
title: 'A Guide to Responsive Web Design',
category: 'Web Design',
description:
'Learn the principles and techniques of responsive web design to create websites that look great on all devices.',
content,
},
{
id: '0e332425-b4f3-4887-b924-35bd10de44be',
title: 'Exploring Python Decorators',
category: 'Python',
description:
'Discover the power of decorators in Python and how they can be used to modify the behavior of functions.',
content,
},
{
id: 'd5be6ade-be0f-4588-b474-aa9381f7f669',
title: 'Mastering CSS Grid Layout',
category: 'CSS',
description:
'A comprehensive guide to CSS Grid Layout, a powerful layout system available in CSS.',
content,
},
{
id: 'c5ac15e4-8806-4fcc-b75a-53e341693da7',
title: 'Building RESTful APIs with Node.js',
category: 'Node.js',
description:
'Learn how to build scalable and efficient RESTful APIs using Node.js and Express.',
content,
},
]
Handling 404
Let's also create a component to render when for handling broken links
import { FC } from 'react'
type NotFoundProps = {
message?: string
}
const NotFound: FC<NotFoundProps> = ({ message }) => {
return <div>{message || 'Oops, page not found!'}</div>
}
export default NotFound
Setup Router
Let's add the router to App.tsx, I am using createBrowserRouter as it is the recommended for web projects like this one.
// src/App.tsx
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
import { Root } from '@/routes/Root'
import { useMemo } from 'react'
import NotFound from './routes/NotFound'
import { Blog } from './routes/Blog'
function App() {
const router = useMemo(
() =>
createBrowserRouter([
{
path: '/',
element: <Root />,
children: [
{
path: '/:id',
element: <Blog />,
},
],
errorElement: (
<NotFound message='Sorry, blog with give ID not found!' />
),
},
{
path: '*',
element: <NotFound />,
},
]),
[]
)
return (
<div className='h-screen bg-gradient-to-r from-violet-500 to-fuchsia-500'>
<RouterProvider router={router} />
</div>
)
}
export default App
Demo
Github repo
You can find the full code in my GitHub repo

