step by step guide to setup the authentication in vite tanstack router app with supabase
First Create the tanstack router boilerplate using this command
pnpm dlx create-tsrouter-app@latest app-name --template file-router
then install supabase js client using
pnpm add @supabase/supabase-js
After intstalling the @supabase/supabase-js
module. create the .env
file in the root of the project and add your supabase credentials in it like this.
VITE_SUPABASE_URL=<your supabase project url> VITE_SUPABASE_ANON_KEY=<your supabase anon key>
Then create the supabase.ts file and create supabase client in it
import { createClient } from “@supabase/supabase-js”; export const supabase = createClient(import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_ANON_KEY);
After creating the supabase client go to __root.ts
file and replace createRootRoute
function with createRootRouteWithContext
and add the auth types in it like this.
import type { User } from “@supabase/supabase-js”; import { Outlet, createRootRouteWithContext } from “@tanstack/react-router”; import { TanStackRouterDevtools } from “@tanstack/react-router-devtools”; export const Route = createRootRouteWithContext<{auth: User | null}>()({ component: () => ( <><Outlet /> <TanStackRouterDevtools /> </>)});
then go to main.ts
file and replace router instance context with this
// Create a new router instance const router = createRouter({ routeTree, context: { auth: null }, defaultPreload: "intent", scrollRestoration: true, defaultStructuralSharing: true, defaultPreloadStaleTime: 0, });
After that create the App()
function in the main.ts
file
function App() { const session = useAuth(); return <RouterProvider router={router} context={{ auth: session }} />; }
here’s the useAuth
hook code.
import { supabase } from "@/supabase"; import type { User } from "@supabase/supabase-js"; import { useEffect, useState } from "react"; function getSupabaseAuthTokenKey(url: string): string { try { const hostname = new URL(url).hostname; const projectId = hostname.split(".")[0]; return `sb-${projectId}-auth-token`; // supabase save session details in localStorage with this type of key format. } catch (error) { throw new Error("Invalid Supabase URL"); } } export function useAuth() { const [session, setSession] = useState<{ user: User | null }>( JSON.parse( localStorage.getItem( getSupabaseAuthTokenKey(import.meta.env.VITE_SUPABASE_URL) ) || "{}" ) || null ); useEffect(() => { const initialize = async () => { const { data } = await supabase.auth.getUser(); setSession(data); }; initialize(); }, []); return session.user; }
then replace the <RouterProvider router-{router}/>
with the <App/>
in root.render()
Method like this.
// Render the app const rootElement = document.getElementById("app"); if (rootElement && !rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement); root.render( <StrictMode> <App /> </StrictMode> ); }
then create a new unauthenticated route file for handling login under the route folder like this routes/(auth)/sign-in.tsx
then paste this code
import { supabase } from "@/supabase"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; export const Route = createFileRoute("/(auth)/sign-in")({ component: SignIn, }); function SignIn() { const navigate = useNavigate({ from: "/sign-in" }); const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); // Prevent the default form submission const formData = new FormData(event.currentTarget); // Get form data const data = { email: formData.get("email"), password: formData.get("password"), }; // Example: validation (optional) if (!data.email || !data.password) { alert("Please fill in all fields"); return; } const { data: sessionData } = await supabase.auth.signInWithPassword({ email: data.email as string, password: data.password as string, }); if (sessionData.session?.user.id) { navigate({ to: "/" }); } }; return ( <form onSubmit={handleSubmit} className="grid gap-3"> <label htmlFor="email">Email</label> <input name="email" type="email" placeholder="name@example.com" required /> <label htmlFor="password">Password</label> <input name="password" type="password" placeholder="********" required /> <button className="mt-2" type="submit"> Login </button> </form> ); }
This is a simple unstyled login form to handle login functionality.
Until now, we have done these steps:
- Created the boilerplate code with Tanstack router + vite.
- Added
.env
variables for the supabase client. - Initialized Supabase client.
- Added the Auth context in the router.
- Created the
useAuth()
hook. - Created the
App()
function inmain.ts
and integrated withinroot.render()
method. - Created the
routes/(auth)/sign-in.tsx
route and implemented the login functionality
Now we, need to create the authenticated routes to check if the authentication really working or not.
We need these steps to done:
- Create the
_authenticated/route.tsx
layout route & implement authentication - Move
routes/index.tsx
toroutes/_authenticated/index.tsx
So then, under the routes
folder create the _authenticated
folder and then create the route.tsx
file. Now the folder structure of routes
folder should look like this.
├── App.css ├── hook │ └── use-auth.tsx ├── logo.svg ├── main.tsx ├── reportWebVitals.ts ├── routes # This is the route folder in which we are working │ ├── (auth) │ │ └── sign-in.tsx │ ├── \_authenticated │ │ ├── index.tsx # "/" protected route │ │ └── route.tsx # The authenticated layout file │ └── \_\_root.tsx ├── routeTree.gen.ts ├── styles.css └── supabase.ts
routes/_authenticated/routes.tsx
file
import { supabase } from "@/supabase"; import { createFileRoute, Outlet, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/_authenticated")({ async beforeLoad({ context: { auth } }) { if (!auth?.id) { const { data } = await supabase.auth.getUser(); if (!data.user?.id) throw redirect({ to: "/sign-in" }); return { auth: data.user }; } }, component: RouteComponent, }); function RouteComponent() { return ( <div> Hello from authenticated route! <Outlet /> </div> ); }
routes/_authenticated/index.tsx
file
import { createFileRoute } from "@tanstack/react-router"; import logo from "../../logo.svg"; import "../../App.css"; export const Route = createFileRoute("/_authenticated/")({ component: AuthenticatedRoute, }); function AuthenticatedRoute() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/routes/\_authenticated/index.tsx</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> <a className="App-link" href="https://tanstack.com" target="_blank" rel="noopener noreferrer" > Learn TanStack </a> </header> </div> ); }
So, That’s it. We integrated supabase authentication with the tanstack router + vite
So if you wanna see the source code you can get it from my GitHub repository.
https://github.com/Your-Ehsan/tutorials/tree/setup-auth-in-react-with-tanstack-and-supabase
or download the code
git clone https://github.com/Your-Ehsan/tutorials.git --branch setup-auth-in-react-with-tanstack-and-supabase --single-branch supabase-auth-with-tanstack
Top comments (0)