How to setup the Supabase authentication with Tanstack Router in Vite React.

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 in main.ts and integrated within root.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 to routes/_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

Similar Posts