T
TanStack4mo ago
afraid-scarlet

Smooth Route Transitions in TanStack Router

Hey everyone! I'm working on a chat application with TanStack Router and struggling with achieving smooth transitions between routes without unwanted remounting. Here's my situation: Current Route Structure:
typescript// Three main routes:
/(app)/chat/ // Chat index/landing page
/(app)/chat/new // New chat page
/(app)/chat/$chatId // Existing chat page
typescript// Three main routes:
/(app)/chat/ // Chat index/landing page
/(app)/chat/new // New chat page
/(app)/chat/$chatId // Existing chat page
2 Replies
afraid-scarlet
afraid-scarletOP4mo ago
The Problem: When a user starts a new chat on /chat/new, the server returns a new chat ID in the response headers. I want to smoothly transition the URL from /chat/new to /chat/abc123 WITHOUT remounting the component, so animations and state are preserved. Current Implementation (that doesn't work):
typescript// In new.tsx - useChat fetch handler
fetch: async (url, options) => {
const response = await fetch(url, options);

if (response.ok && !hasNavigated) {
const newChatId = response.headers.get('X-Chat-Id');
if (newChatId && !hasNavigated) {
setHasNavigated(true);

navigate({
to: '/chat/$chatId',
params: { chatId: newChatId },
replace: true,
resetScroll: false,
});
}
}
return response;
}
typescript// In new.tsx - useChat fetch handler
fetch: async (url, options) => {
const response = await fetch(url, options);

if (response.ok && !hasNavigated) {
const newChatId = response.headers.get('X-Chat-Id');
if (newChatId && !hasNavigated) {
setHasNavigated(true);

navigate({
to: '/chat/$chatId',
params: { chatId: newChatId },
replace: true,
resetScroll: false,
});
}
}
return response;
}
What I've Tried (and why they failed): Route Masking - Used mask option but the chat never gets the new ID page, stays on new chat page Shared Component Approach - Single component handling both new/existing, but I DO want remounting when navigating TO /chat/new (for fresh state), just not when transitioning FROM new to existing Various navigation options - replace: true, resetScroll: false, etc. Current Route Definitions:
typescript// new.tsx
export const Route = createFileRoute('/(app)/chat/new')({
component: NewChatPage,
});

// $chatId.tsx
export const Route = createFileRoute('/(app)/chat/$chatId')({
component: ChatPage,
});
typescript// new.tsx
export const Route = createFileRoute('/(app)/chat/new')({
component: NewChatPage,
});

// $chatId.tsx
export const Route = createFileRoute('/(app)/chat/$chatId')({
component: ChatPage,
});
Is there a way in TanStack Router to transition between different route components smoothly without remounting? Or should I be structuring this differently? I've read about the router previously using route keys that cause remounting, but I need the remounting behavior for new chats, just not for this specific transition. Any ideas or alternative approaches would be greatly appreciated!
eastern-cyan
eastern-cyan4mo ago
can you get into more detail about the shared component approach? when is it not remounting? are you maybe looking for remountDeps ?

Did you find this page helpful?