Skip to content

React Router (Vue Router in a trench coat) ​

Routes, nested layouts, params - same problems. Different file of configuration.

We'll use React Router v6/v7 style APIs (the createBrowserRouter world). Install:

bash
npm install react-router

(Package name has been consolidating - follow the current React Router docs for your major version. The concepts below stay stable.)

Basic setup ​

tsx
// main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router'
import App from './App'
import Home from './pages/Home'
import About from './pages/About'

const router = createBrowserRouter([
  {
    path: '/',
    element: <App />,
    children: [
      { index: true, element: <Home /> },
      { path: 'about', element: <About /> },
    ],
  },
])

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>,
)

Comments for the code above:

  • Nested children ≈ Vue Router nested routes + layout
  • index: true ≈ empty child path / default child
  • App should render an <Outlet /> where children appear

Layout with Outlet ​

tsx
import { Outlet, Link } from 'react-router'

export default function App() {
  return (
    <div>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <Outlet />
    </div>
  )
}

As you can see - <Outlet /> is <RouterView />. <Link> is <RouterLink>.

Params ​

tsx
// route: { path: 'users/:id', element: <UserPage /> }

import { useParams } from 'react-router'

function UserPage() {
  const { id } = useParams()
  return <p>User {id}</p>
}

Same energy as route.params.id / useRoute().params.

tsx
import { useNavigate } from 'react-router'

const navigate = useNavigate()
navigate('/about')
navigate(-1) // back

≈ router.push / router.back.

Vue Router → React Router cheat ​

Vue RouterReact Router
RouterViewOutlet
RouterLinkLink
useRoute()useParams, useLocation, useMatch
useRouter().pushuseNavigate()
navigation guardsloaders / actions / your own wrappers (or auth layout routes)

Auth-ish layout (simple pattern) ​

tsx
{
  path: '/app',
  element: <RequireAuth><AppLayout /></RequireAuth>,
  children: [
    { path: 'dashboard', element: <Dashboard /> },
  ],
}

RequireAuth checks a token/context and either renders children or <Navigate to="/login" replace />. Not as fancy as global beforeEach, but clear.

We'll touch loaders lightly later if you go Next instead - for Vite SPAs, fetch in components or TanStack Query is the usual path I've seen.

Next: fetching without a library - then why you'll want one.

Enjoy!