Render modal on a route with the parent route rendered in background in Tanstack Router
Modals are one of the most versatile UI patterns in modern web development. They allow us to present focused content without losing context of the underlying page. And it also helps us to improve the user experience and SEO.
Today, I’ll walk you through creating modal routes using Tanstack Router - a powerful, type-safe routing solution for React applications. We’ll build a gallery where clicking on images opens them in a modal while keeping the gallery visible in the background.
Why Modal Routes Matter 🎯
Modal routes are perfect for scenarios where you want to:
- Show detailed views: Display item details, user profiles, or product information without losing the list context
- Handle forms: Present forms for editing, creating, or configuring while keeping the parent page accessible
- Improve UX: Maintain user context and reduce navigation friction
- Deep linking: Allow users to share direct links to modal content
- SEO: Better for SEO as the modal is a separate route
You can do the same thing with react-router-dom and Next.js. you can checkout those articles later.
Prerequisites 📋
Before we dive in, make sure you have:
- A basic understanding of React and TypeScript
- Familiarity with Tanstack Router concepts (if not, check their excellent documentation)
- A React project set up with TypeScript support
We’ll be building a product photo gallery application where users can browse images and click to view them in a modal overlay.
Install Tanstack Router
First, let’s set up Tanstack Router in our project. We’ll use the createRouter
function to define our routes.
npm install @tanstack/react-router
# or
pnpm add @tanstack/react-router
# or
yarn add @tanstack/react-router
So since in tanstack router we can implement the routing in two ways, file based and code based.
I will not write the code here i will provide the github repo link. so you can explore and learn from it.
File Based Routing
In file based routing we can create a folder and inside that folder we can create a file for each route or we can use the .
for child routes like in the example below.
and the whole code is available in the github repo .
- __root.tsx
- about.tsx
- index.tsx
- photos_$id.tsx
- photos.{$id}_modal.tsx
- photos.tsx
Code Based Routing
In other hand we can use the code based routing to create the routes. code based routing works similar to react-router-dom.
The whole code is available in the github repo .
- __root.tsx
- about.tsx
- index.tsx
- photos-id.tsx
- photos-modal.tsx
- photos.tsx
- main.tsx
This is how the main file should look like.
import { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import {
createRouteMask,
createRouter,
RouterProvider,
} from '@tanstack/react-router'
// ROUTES
import { rootRoute } from './routes/__root'
import { aboutRoute } from './routes/about'
import { indexRoute } from './routes/index'
// PHOTOS ROUTES
import { photosRoute } from './routes/photos'
import { photoChildRoute } from './routes/photos-id'
import { photoModalRoute } from './routes/photos-modal'
const routeTree = rootRoute.addChildren([
indexRoute,
aboutRoute,
photoChildRoute,
photosRoute.addChildren([photoModalRoute]),
])
// Create a route mask for the photo modal to photo route
const photoModalToPhotoMask = createRouteMask({
routeTree,
from: '/photos/$id/modal',
to: '/photos/$id',
params: true,
})
// Set up a Router instance
const router = createRouter({
routeTree,
routeMasks: [photoModalToPhotoMask],
scrollRestoration: true,
})
// Register the router instance for type safety
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
// Render the app
const rootElement = document.getElementById('root')!
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement)
root.render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
)
}
This is how the root file looks like.
import { createRootRoute, Outlet } from '@tanstack/react-router'
import Navbar from '../_components/navbar'
type PhotoModal = {
id: 'photo'
photoId: string
}
type ModalObject = PhotoModal
export const rootRoute = createRootRoute({
validateSearch: (search) =>
search as {
modal?: ModalObject
},
component: RootComponent,
})
function RootComponent() {
return (
<>
<Navbar />
<Outlet />
</>
)
}
What We’ve Built 🎉
Congratulations! You’ve successfully implemented a modal route system using Tanstack Router. Here’s what we accomplished:
- Type-safe routing: All routes and parameters are fully typed
- Nested routing: The modal renders as a child route while preserving the parent
- Deep linking: Modal URLs are shareable and bookmarkable
- Smooth UX: Users can navigate back to the gallery without losing context
Key Benefits of Tanstack Router
- File-based routing: Routes are defined in separate files for better organization
- Type safety: Full TypeScript support with automatic type inference
- Modern patterns: Built for React 18+ with concurrent features
- Developer experience: Excellent tooling and debugging capabilities
Conclusion
I hope you find this article helpful.
You can checkout the github repo for the code.
Happy coding! 🚀
Comments
Leave a comment or reaction below!