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

EHSAN. @developerehsan

About: Hello, my name is Ehsan Shahid, and I am interested in learning about Modern websites and Web-apps. I am full-stack website developer and currently learning about NestJS.

Location:
Pakistan
Joined:
Dec 4, 2021

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

Publish Date: Jul 16
6 0

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
Enter fullscreen mode Exit fullscreen mode

then install supabase js client using

pnpm add @supabase/supabase-js
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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 />  
</>)});
Enter fullscreen mode Exit fullscreen mode

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,  
});
Enter fullscreen mode Exit fullscreen mode

After that create the App() function in the main.ts file

function App() {  
  const session = useAuth();  
  return <RouterProvider router={router} context={{ auth: session }} />;  
}
Enter fullscreen mode Exit fullscreen mode

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;  
}
Enter fullscreen mode Exit fullscreen mode

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>  
  );  
}
Enter fullscreen mode Exit fullscreen mode

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>  
  );  
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>  
  );  
}
Enter fullscreen mode Exit fullscreen mode

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>  
  );  
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Comments 0 total

    Add comment