navigate to external url with tanstack router / start
Which project does this relate to?
Router
Describe the bug
Hey everyone,
I couldnt find how to navigate to an external url using tanstack router / start. I tried to search in the docs for it but couldn't find anything.
I am currently doing it in this way but I am wondering if tanstack router supports a better way to navigate to an external url ? Especially with the new useTransition hook that can be used to track redirecting, which is very usefull to show some sort of spinner while redirecting.
function Home() {
const [isRedirectPending, startRedirectTransition] = useTransition();
const navigatingTo = (url: string) => {
startRedirectTransition(async () => {
window.location.href = url;
});
};
return (
<main>
<button
onClick={() => {
navigatingTo('https://stopjava.com/');
}}
>
redirect to site
</button>
{isRedirectPending && <p>redirecting...</p>}
</main>
);
}
Your Example Website or App
https://stackblitz.com/edit/github-ynarx9vs?file=app%2Froutes%2Findex.tsx
Steps to Reproduce the Bug or Issue
- set the network speed to 3g and disabled caching in the network tab
- click the navigate button from the repro link above
Expected behavior
- navigating to an external url
- using the useTransition hook to track redirecting
Screenshots or Videos
https://github.com/user-attachments/assets/6a896f76-23a6-4839-b51f-f3daa8ed54ad
Platform
- OS: Nixos
- Browser: Chrome
- Version: 131.0.6778.204 (Official Build) (64-bit)
Additional context
No response
You can just use the native html <a href="" /> tag since you don't need any of the typesafety you use for internal routes
Yes your are correct but this is a simplifed example.
This issue will happen for example when setting up oauth. With the example below I just can't use an anchor unfortunately:
const useGithubSignIn = () => {
const [isRedirectPending, startRedirectTransition] = useTransition();
const githubSignIn = useMutation({
mutationFn: githubSignInAction,
onSuccess: (url) => {
startRedirectTransition(async () => {
window.location.href = url;
});
},
});
return {
...githubSignIn,
isPending: githubSignIn.isPending || isRedirectPending,
};
};
@thibaultdenis014 why do you think router should support this? The main purpose of Tanstack Router is to help you to navigate on your own site
Nextjs supports this, and it gives a very nice user experience when redirecting to antoher website.
For example when using oauth, I want to display a loading spinner when the user is getting redirected (as those sort of redirects take around 1/2 sec with a decent connection). Below is an example of doing that with nextjs + the useTransition hook to monitor the redirecting. Which is what I want to reproduce with tanstack router.
//nextjs - not tanstack router / start
export const GithubSignIn = () => {
const router = useRouter();
const [isRedirectPending, startRedirectTransition] = useTransition();
const githubSignIn = api.auth.githubSignIn.useMutation({
onSuccess: (url) => {
startRedirectTransition(() => {
router.push(url.href);
});
},
});
return (
<Button
className="mt-3 w-full gap-2 bg-black font-bold text-white hover:bg-black/80"
disabled={githubSignIn.isPending || isRedirectPending}
onClick={() =>
githubSignIn.mutate({ callbackUrl: searchParams.get("callback-url") })
}
>
<GithubIcon />
<span>github</span>
{(githubSignIn.isPending || isRedirectPending) && <Spinner />}
</Button>
)}
);
};
was looking for something simiar, found this note on the NavigateOptionsType re: the href property
"...navigate to a fully built href, e.g. pointing to an external target.`
It didn't appear to be working at first but dug into the code and seems like the href param is only considered when the reloadDocument option is true, at which point it works.
(under the hood its just assigning window.location the same way you are though 🤷 )
eg
const nav = useNavigate();
if(shouldRedirect) {
nav({
href: "https://github.com/TanStack/router/issues/3232",
reloadDocument: true,
})
}
Important to note that with Tanstack Start we need to find a way to avoid using window as the function could be used during SSR on the server. I am working around this by redirecting to a dedicated page where use a useEffect where I do the client-side redirect.
Something like:
beforeLoad: async () => {
if (redirectNecessary) {
/**
* when the page is loaded initally beforeLoad only runs on the server where we can't
* redirect to an external route right now:
*/
throw redirect({
to: "/sso-redirect",
replace: true,
});
}
},
and in sso-redirect:
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { LoadingBox } from "../../../components/progress/LoadingBox";
import { useInitEffect } from "../../../hooks/useInitEffect";
import { getConfig } from "../../../queries/config/config.query.helper";
import { createSSORedirectPath } from "../../../utils/soo.utils";
export const Route = createFileRoute("/_baseLayout/(landing)/sso-redirect")({
component: RouteComponent,
});
function RouteComponent() {
const navigate = useNavigate();
useEffect(() => {
navigate({
href: "https://tanstack.com",
reloadDocument: true,
});
},[]);
return <LoadingBox />;
}
@flodaniel you can just throw a redirect to an external url:
throw redirect({
href: "https://tanstack.com",
});
@schiller-manuel of course you where right! I checked our code again and we had an unchecked window.location usage burried somewhere. Your provided example works without a problem!
Nextjs supports this, and it gives a very nice user experience when redirecting to antoher website.
For example when using oauth, I want to display a loading spinner when the user is getting redirected (as those sort of redirects take around 1/2 sec with a decent connection). Below is an example of doing that with nextjs + the
useTransitionhook to monitor the redirecting. Which is what I want to reproduce with tanstack router.//nextjs - not tanstack router / start export const GithubSignIn = () => { const router = useRouter(); const [isRedirectPending, startRedirectTransition] = useTransition();
const githubSignIn = api.auth.githubSignIn.useMutation({ onSuccess: (url) => { startRedirectTransition(() => { router.push(url.href); }); }, });
return ( <Button className="mt-3 w-full gap-2 bg-black font-bold text-white hover:bg-black/80" disabled={githubSignIn.isPending || isRedirectPending} onClick={() => githubSignIn.mutate({ callbackUrl: searchParams.get("callback-url") }) } > <GithubIcon /> github {(githubSignIn.isPending || isRedirectPending) && <Spinner />} </Button> )} ); };
Hi, has anyone found a solution or a workaround for this scenario? I would also like a way to show a loading spinner when redirecting a user to an external oauth/stripe/etc link since it does take a few seconds.