Next.js Discord

Discord Forum

Error after wrapping my layout so that I can use Zustand and auth state globally

Answered
Sun bear posted this in #help-forum
Open in Discord
Sun bearOP
//app/[lang]/layout.tsx
import React, { useEffect } from 'react';
import '../globals.css'
import { ThemeProvider } from "@/components/theme-provider"
import { PropsWithChildren } from 'react'
import TopNavigationBar from '@/components/TopNavigationBar'
import { SvgGradients } from '@/components/svg-gradient'
import fontVariables from '@/components/fonts'
import { createClient } from '../../utils/supabase/client';
import useUserSessionStore from '../../utils/supabase/authState';

const defaultUrl = process.env.NODE_ENV === "production" ? `https://${process.env.VERCEL_URL}` : "https://www.jobsbringer.com"

export const metadata = {
  metadataBase: new URL(defaultUrl),
  title: 'Next.js and Supabase Starter Kit',
  description: 'The fastest way to build apps with Next.js and Supabase',
}

export default function RootLayout({ children }: PropsWithChildren) {
  const { setUser } = useUserSessionStore();
  const supabase = createClient();

  useEffect(() => {
    const fetchUser = async () => {
      const { data: user, error } = await supabase.auth.getUser();
      if (error) {
        console.error('Error fetching user:', error);
        return;
      }
      setUser(user);
    };

    fetchUser();

    const { data: listener } = supabase.auth.onAuthStateChange(async (event, session) => {
      const { data: updatedUser } = await supabase.auth.getUser();
      setUser(updatedUser);
    });

    return () => {
      listener.subscription.unsubscribe();
    };
  }, [setUser]);

  return (
    <html lang="en" className={fontVariables} suppressHydrationWarning>
      <body className="font-roboto h-[100svh]">
        <SvgGradients/>
        <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
          <main className="flex flex-col flex-grow items-center h-full">
            <TopNavigationBar/>
            {children}
          </main>
        </ThemeProvider>
      </body>
    </html>
  );
}

How can I fix this error without unwrapping my layout ?
Answered by Russian Spaniel
Have a providers.tsx type file in your layout like how Rafael mentioned. Make it a client component.

Then you simply get the user Session from supabase and you store it using zustand.

That's all you need to do.
View full answer

340 Replies

You are using useEffect
Cant do that in a client component.
Sun bearOP
I know but how can I get around that without unwrapping the app.
// utils/supabase/authState.js

import create from 'zustand';

const useUserSessionStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

export default useUserSessionStore;
Can I just move the logic in this file instead or something ?
Do you really need to use useEffect there? Does the fetch not work without it?

If it doesnt, then move the component that uses the information and the fetching into a separate component and fetch there. Else you'll need to make your layout a client component.

(i have not taken a good look at the code, im on mobile and it's kinda annoying)
Sun bearOP
No idea.
Bruh wdym no idea. Try it out without the useEffect
@Clown Bruh wdym no idea. Try it out without the useEffect
Sun bearOP
It doesn't work without useEffect the way I currently have it.
As in it starts throwing errors in the code.
Sun bearOP
I'm using supabase auth through supabase/ssr
https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=server-component

That's what I've already done by the way in order to set-up Supabase
What I'm trying to do at the moment is getting rid by the useEffect method I was trying to use before (when I started this thread) and instead adding this this code snippet in my root layout file in order to globally monitor the auth state of the users (if they are logged in or not basically):
supabase.auth.onAuthStateChange(async (event, session) => {
      const { data: updatedUser } = await supabase.auth.getUser();
      setUser(updatedUser);
    });

However upon asking GPT to help me with incorporating that snippet into my root layout file it said the following, which looks like a bit like a warning that I may be doing something in a way it's not optimal:
So I'd like to know if GPT has a point here and if it's right what should I do instead, given the context of what I'm trying to do.
I'm also not sure how Zustand would play into this since that code snippet is not related to it, unless it's somehow connected to this file:
// utils/supabase/authState.js

import create from 'zustand';

const useUserSessionStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

export default useUserSessionStore;
Sun bearOP
So GPT was right, thanks God it warned me then.
I was about to go ahead and do it that way if it wasn't for it telling me "hey dude, what are you doing"
So how should I go about this instead, if you know ?
what you need to do is to create a client component that can use this useEffect and render it in the RootLayout, like how you are doing with the ThemeProvider
Sun bearOP
I see. I already have a client component which looks like this:
//utils/supabase/client.ts

import { createBrowserClient } from '@supabase/ssr'

export const createClient = () =>
  createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )

If what you mean is a supabase client component.
And a server component for supabase which looks like this:
//utils/supabase/server.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'

export const createClient = (cookieStore: ReturnType<typeof cookies>) => {
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options })
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  )
}
So what should I do and in which one of these 2 files ?
I guess these are called utilities, right ?
these are not components, I meant react components
Sun bearOP
So I should create a new file instead under my components folder ?
yeah
I don't use supabase so idk how they handle auth, but to solve your problem about useEffect all you need to do is to create a new client component and wrap the stuff in RootLayout with it
@Rafael Almeida I don't use supabase so idk how they handle auth, but to solve your problem about `useEffect` all you need to do is to create a new client component and wrap the stuff in `RootLayout` with it
Sun bearOP
Okay thank you very much, I'll take this one back to Supabase support and see if they can help me moving forward.
As in, I'll let them know what you said.
I'll post here the solution if they manage to help me implement this.
Russian Spaniel
Have a providers.tsx type file in your layout like how Rafael mentioned. Make it a client component.

Then you simply get the user Session from supabase and you store it using zustand.

That's all you need to do.
Answer
Russian Spaniel
You need to do this, because you are using createBrowserClient.

I'm using supabase myself and using createServerClient. However, I only call supabase in all my server components and then pass down the result to the client components to use.
@Russian Spaniel Have a providers.tsx type file in your layout like how Rafael mentioned. Make it a client component. Then you simply get the user Session from supabase and you store it using zustand. That's all you need to do.
Sun bearOP
Hold on, I just realised I have another supabase file I forgot about.

// context/AuthContext.js

import { createContext, useContext, useState, useEffect } from 'react';
import { supabase } from '../utils/supabaseClient';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    // Recover session if page is refreshed
    const session = supabase.auth.session();
    setUser(session?.user ?? null);

    // Listen for changes on authentication state (sign in or sign out)
    const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
      setUser(session?.user ?? null);
    });

    // Cleanup the listener when component is unmounted
    return () => {
      authListener?.unsubscribe();
    };
  }, []);

  const logout = async () => {
    await supabase.auth.signOut(); // Sign out with Supabase
    setUser(null); // Update the user state to null
  };

  return (
    <AuthContext.Provider value={{ user, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  return useContext(AuthContext);
};

Is this what you mean ?
Russian Spaniel
So much boilerplate. You don't need any of this. 🤦‍♂️
Why are you listening to the signout? Are you intending to have the redirect go to itself or back to the login page?
Sun bearOP
It's a file provided by the Supabase starter kit, I did not wrote it myself.
So can I just delete it ?
Or should I refactor it instead ?
Russian Spaniel
You should be asking yourself what you want to achieve. For example, when I log out. I clear the session and redirect back to the root. I am not watching or listening to any events.
@Russian Spaniel You should be asking yourself what you want to achieve. For example, when I log out. I clear the session and redirect back to the root. I am not watching or listening to any events.
Sun bearOP
That's what I want it to do as well, because that saves me headache when they log-out on a page they can only access if they are logged in.
Russian Spaniel
This is what I am currently doing for my logout button.

import { createSupabaseServerClient } from "@acme/auth"
import { redirect } from "next/navigation";

async function logout(): Promise<any> {
    "use server";
    const supabase = createSupabaseServerClient();

  const { data } = await supabase.auth.getSession();

    if (data.session === null) {
        return {
            error: "Unauthorized"
        };
    }

    const { error } = await supabase.auth.signOut();

    if (error === null) {
        redirect("/");
    }
}

export default async function LogoutButton() {
  return (
    <form action={logout}>
            <button>Sign out</button>
        </form>
  );
}
It can easily be refactored if you need extra things. I'm working on a poc with supabase.
I also highly recommend:

npx create-next-app -e with-supabase

into a directory to pull an example app.

there you can see how they (supabase) use clientServer and clientBrowser.
I recommend only using the createSupabaseServerClient in the layout.tsx. Which you can then drop into a providers.tsx which has a use client and then using zustand can set it into state. You might be able to get away without needing a useEffect. Not sure 🤷‍♂️

You certainly do not need a context. That defeats the purpose of needing zustand.
@Russian Spaniel I also highly recommend: > npx create-next-app -e with-supabase into a directory to pull an example app. there you can see how they (supabase) use clientServer and clientBrowser.
Sun bearOP
Can you help me with the code for the provider file (if it's small). I'd really appreciate that cause I don't know how to do it.
I want it to work like you said, with Zustand and Supabase.
The only file with Zustand I have right now is this one, I don't even know if I need it:
// utils/supabase/authState.js

import create from 'zustand';

const useUserSessionStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

export default useUserSessionStore;
Russian Spaniel
"use client";
import { useEffect } from "react";
import useUserSessionStore from "./utils/supabase/authState.js";

interface SessionProviderProps {
  session: any;
  children: React.ReactNode;
}

function SessionProvider({session, children}: SessionProviderProps) {
  const { setUser } = useUserSessionStore();

  useEffect(() => {
     setUser(session)
  }, []);

  return <>{children}</>;
}
I don't use Zustand. So this is all off the top of my head lol
Sun bearOP
No worries, if it doesn't work out of the box I can try and refactor it with GPT, but having a good foundation for things helps alot to avoid having GPT hallucinating solutions.
Russian Spaniel
Also you need to have this in your layout.tsx:

const supabase = createSupabaseServerClient();
const { session } = await supabase.auth.getUser();

return (
    <html lang="en">
      <body>
        <SessionProvider session={session}>
          {children}
        </SessionProvider>
      </body>
    </html>
  );
Oh, I suppose I just need to import the useEffect stuff right ?
I mean the statement
Russian Spaniel
yeah, I forgot about that. It's late for me right now.
I updated my code.
And in my layout file what imports do I need to use for this to work ?
As in what files do I need to import
Russian Spaniel
That's the thing. I think you are using the "Browser" function from supabase right? You'll need to move over to the "Server" functionality.
I'll let you know if you let me know where to check
Russian Spaniel
export const createClient = () =>
  createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
You need to import the createClient that uses the createServerClient
Looks like it's this one in your code:

//utils/supabase/server.ts
import { createClient } from "utils/supabase/server";
So the full code should be something like:

import { createClient } from "utils/supabase/server";
const supabase = createClient();
const { session } = await supabase.auth.getUser();

return (
    <html lang="en">
      <body>
        <SessionProvider session={session}>
          {children}
        </SessionProvider>
      </body>
    </html>
  );
Of course, your layout will be different.
Sun bearOP
//utils/supabase/client.ts

import { createBrowserClient } from '@supabase/ssr'

export const createClient = () =>
  createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
`
This is my client file I think
And this is my server file:
//utils/supabase/server.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'

export const createClient = (cookieStore: ReturnType<typeof cookies>) => {
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options })
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  )
}
And this is my layout file:
//app/[lang]/layout.tsx
import '../globals.css'
import { ThemeProvider } from "@/components/theme-provider"
import { PropsWithChildren } from 'react'
import TopNavigationBar from '@/components/TopNavigationBar'
import { SvgGradients } from '@/components/svg-gradient'
import fontVariables from '@/components/fonts'
import { createClient } from '@/utils/supabase/server'

const defaultUrl = process.env.NODE_ENV === "production" ? `https://${process.env.VERCEL_URL}` : "https://www.jobsbringer.com"

export const metadata = {
  metadataBase: new URL(defaultUrl),
  title: 'Next.js and Supabase Starter Kit',
  description: 'The fastest way to build apps with Next.js and Supabase',
}

const supabase = createSupabaseServerClient();
const { session } = await supabase.auth.getUser();

return (
    <html lang="en">
      <body>
        <SessionProvider session={session}>
          {children}
        </SessionProvider>
      </body>
    </html>
  );

export default function RootLayout({
  children,
}: PropsWithChildren) {
  return (
    <html lang="en" className={fontVariables} suppressHydrationWarning>
      <body className="font-roboto h-[100svh]">
        <SvgGradients/>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
        >
          {/* Page Background */}
          <main className="flex flex-col flex-grow items-center h-full">
            <TopNavigationBar/>
            {children}
          </main>
        </ThemeProvider>
      </body>
    </html>
  );
}
And here is telling me it's not working
@Sun bear And here is telling me it's not working
Russian Spaniel
You've got these errors because I'm lazily writing in pseudo code. You are going to have to fill in the blanks like async and the imports for functions.
I don't have access to your codebase, so I don't know where things are.
Also don't just copy and paste what I'm writing. It doesn't work that way. 😄
Figure out the difference with the code that you have and what I have supplied.
@Russian Spaniel export const createClient = () => createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! )
Sun bearOP
//utils/supabase/client.ts

import { createBrowserClient } from '@supabase/ssr'

export const createClient = () =>
  createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )

I think this is the file you mean, but mine uses the browser function as you can see. How can i move over to server functionality ? Do I need to use the server file instead or do I need to refactor this client.ts file ?
Russian Spaniel
If you import utils/supabase/client.ts that uses the BrowserClient.

So import from utils/supabase/server.ts instead.
Sun bearOP
//app/[lang]/layout.tsx
import '../globals.css'
import { ThemeProvider } from "@/components/theme-provider"
import { PropsWithChildren } from 'react'
import TopNavigationBar from '@/components/TopNavigationBar'
import { SvgGradients } from '@/components/svg-gradient'
import fontVariables from '@/components/fonts'
import { createClient } from '@/utils/supabase/server'

const defaultUrl = process.env.NODE_ENV === "production" ? `https://${process.env.VERCEL_URL}` : "https://www.jobsbringer.com"

export const metadata = {
  metadataBase: new URL(defaultUrl),
  title: 'Next.js and Supabase Starter Kit',
  description: 'The fastest way to build apps with Next.js and Supabase',
}

const supabase = createSupabaseServerClient();
const { session } = await supabase.auth.getUser();

export default function RootLayout({
  children,
}: PropsWithChildren) {
  return (
    <html lang="en" className={fontVariables} suppressHydrationWarning>
      <body className="font-roboto h-[100svh]">
      <SessionProvider session={session}>
        <SvgGradients/>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
        >
          {/* Page Background */}
          <main className="flex flex-col flex-grow items-center h-full">
            <TopNavigationBar/>
            {children}
          </main>
        </ThemeProvider>
        </SessionProvider>
      </body>
    </html>
  );
}

I have refactored it to use the server file instead but I still get the errors
Russian Spaniel
//app/[lang]/layout.tsx
import '../globals.css'
import { ThemeProvider } from "@/components/theme-provider"
import { PropsWithChildren } from 'react'
import TopNavigationBar from '@/components/TopNavigationBar'
import { SvgGradients } from '@/components/svg-gradient'
import fontVariables from '@/components/fonts'
import { createClient } from '@/utils/supabase/server'

const defaultUrl = process.env.NODE_ENV === "production" ? `https://${process.env.VERCEL_URL}` : "https://www.jobsbringer.com"

export const metadata = {
  metadataBase: new URL(defaultUrl),
  title: 'Next.js and Supabase Starter Kit',
  description: 'The fastest way to build apps with Next.js and Supabase',
}

export default function RootLayout({
  children,
}: PropsWithChildren) {
  const supabase = createClient();
  const { session } = await supabase.auth.getUser();

  return (
    <html lang="en" className={fontVariables} suppressHydrationWarning>
      <body className="font-roboto h-[100svh]">
        <SessionProvider session={session}>
          <SvgGradients/>
          <ThemeProvider
            attribute="class"
            defaultTheme="system"
            enableSystem
          >
            {/* Page Background */}
            <main className="flex flex-col flex-grow items-center h-full">
              <TopNavigationBar/>
              {children}
            </main>
          </ThemeProvider>
        </SessionProvider>
      </body>
    </html>
  );
}
Sun bearOP
Russian Spaniel
import { createClient } from '@/utils/supabase/server'
const supabase = createSupabaseServerClient();


it's incorrect because you aren't importing createClient.
export default async function
Your createClient on the server has this:

export const createClient = (cookieStore: ReturnType<typeof cookies>) => {


this is why the param is marked as missing
@Russian Spaniel js export default async function
Sun bearOP
Do I need to put this in the server file ?
Russian Spaniel
@Sun bear Do I need to put this in the server file ?
Russian Spaniel
Look at the top of layout.tsx
@Russian Spaniel Look at the top of layout.tsx
Sun bearOP
Does this look good to you now ?
// components/providers.tsx
"use client";
import React, { useEffect } from "react";
import useUserSessionStore from "../utils/supabase/authState";

interface SessionProviderProps {
  session: any;
  children: React.ReactNode;
}

function SessionProvider({ session, children }: SessionProviderProps) {
  const { setUser } = useUserSessionStore();

  useEffect(() => {
    if (session) {
      setUser(session.user);
    }
  }, [session, setUser]);

  return <>{children}</>;
}

export default SessionProvider;
I've formatted it with the help of GPT since the export statement didn't work for me initially.
Russian Spaniel
You don't need the if statement. If they don't have a session, you should be handling that in the middleware
Sun bearOP
Russian Spaniel
The way this flows is like this.
- middleware, this checks whether they have a session. If they don't. they get redirected.
- on the page. now they are certified to see the route. so get the session again in the providers and assign it to zustand.
sadly with nextjs, you can't pass things from the middleware to the page 🤷‍♂️
Russian Spaniel
You should focus on doing 1 thing at a time.
Sort the sessionsProvider.
Then sort the layout.tsx
then do the middleware
Sun bearOP
Yes I'm only trying to update the provider to work like you said.
Russian Spaniel
I strongly recommend doing npx create-next-app -e with-supabase in a folder and checking out how supabase does auth. This should give you a few answers on how to approach your app. 😄
Sun bearOP
But I don't know how to add the export statement cause if I use the code you gave me and simply add the export statement it will give errors, that's why I was asking GPT to help me.
Russian Spaniel
you dont add the export statement
Sun bearOP
Then why did you mention it ?
I thought you wanted me to add it 😄
Russian Spaniel
your export default has red lines right?
in layout.tsx?
or did you fix it?
@Russian Spaniel your export default has red lines right?
Sun bearOP
Here is what happens
Russian Spaniel
remove that line
simply have export default function SessionProvider(...) { at the top.
or have export default function SessionProvider at the bottom.

It's really a matter of choice.
@Russian Spaniel simply have `export default function SessionProvider(...) {` at the top.
Sun bearOP
// components/providers.tsx
"use client";
import useUserSessionStore from "../utils/supabase/authState.js";
import { useEffect } from "react";

interface SessionProviderProps {
  session: any;
  children: React.ReactNode;
}

export default function SessionProvider({ session, children }: SessionProviderProps) {
  const { setUser } = useUserSessionStore();

  useEffect(() => {
    setUser(session);
  }, [session, setUser]);

  return <>{children}</>;
}

Is this correct ?
Russian Spaniel
yeah looks good.
what about layout.tsx ?
@Russian Spaniel what about layout.tsx ?
Sun bearOP
//app/[lang]/layout.tsx
import '../globals.css'
import { ThemeProvider } from "@/components/theme-provider"
import { PropsWithChildren } from 'react'
import TopNavigationBar from '@/components/TopNavigationBar'
import { SvgGradients } from '@/components/svg-gradient'
import fontVariables from '@/components/fonts'
import { createClient } from '@/utils/supabase/server'
import { SessionProvider } from '@/components/providers'

const defaultUrl = process.env.NODE_ENV === "production" ? `https://${process.env.VERCEL_URL}` : "https://www.jobsbringer.com"

export const metadata = {
  metadataBase: new URL(defaultUrl),
  title: 'Next.js and Supabase Starter Kit',
  description: 'The fastest way to build apps with Next.js and Supabase',
}

const supabase = createSupabaseServerClient();
const { session } = await supabase.auth.getUser();

export default function RootLayout({
  children,
}: PropsWithChildren) {
  return (
    <html lang="en" className={fontVariables} suppressHydrationWarning>
      <body className="font-roboto h-[100svh]">
      <SessionProvider session={session}>
        <SvgGradients/>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
        >
          {/* Page Background */}
          <main className="flex flex-col flex-grow items-center h-full">
            <TopNavigationBar/>
            {children}
          </main>
        </ThemeProvider>
        </SessionProvider>
      </body>
    </html>
  );
}

It looks like this currently
And it gives these errors
Russian Spaniel
change to

export default async function RootLayout
the await red line should then disappear
remove the { SessionProvider } import and then make vsCode reimport the sessionProvider again. it should pull it from the right place.
then change createSupabaseServerClient with createClient
that should resolve your 3 issues
Sun bearOP
I did all that and it still shows the errors
I have deleted the import statement and re-wrote it
Hmm, the issue seems to be with the import statement
But I don't get why, it's pointing to the correct file
Oh do I need to do it without the brackets ?
Russian Spaniel
lol
my bad
remove the curly braces { }
because it's a default
import SessionProvider
At around 22:00 my brain turns into a pumpkin
Sun bearOP
Awesome! That got rid of the import error. There's only a few left now.
Russian Spaniel
I already said how to resolve those.
@Russian Spaniel https://discord.com/channels/752553802359505017/1213394598353838112/1213616443522813952
Sun bearOP
I did do all of that already 😁
Russian Spaniel
See I'm not paying attention
Sun bearOP
It's okay
Russian Spaniel
Move the two const between the

PropsWithChildren) {

and

return (
its because they are not in the function, they should be
Sun bearOP
:thinq:
Russian Spaniel
this is what my Server Client looks like for supabase.

import {cookies} from "next/headers";
import {createServerClient} from "@supabase/ssr";

export function createClient(debug?: boolean) {
  return createServerClient(
    process.env.SUPABASE_URL!,
    process.env.SUPABASE_KEY!,
    {
      global: {
        fetch: (...args) => {
          debug !== undefined && debug && console.log(args);

          return fetch(...args);
        }
      },
      cookies: {
        get(name: string) {
          // return cookie with the 'name' here    
          return cookies().get(name)?.value;      
        },
        set(name: string, value, options) {
          // set cookie
          cookies().set(name, value, options);
        },
        remove(name: string, options) {
          // remove cookie
          cookies().set(name, "", options);
        },       
      },
    }
  );  
};
Maybe try overwriting your code, with mine?
@Russian Spaniel Maybe try overwriting your code, with mine?
Sun bearOP
Yup, it seems to work I just need to install next cookies
Is there a difference between next cookies and how I have it here ?
//utils/supabase/server.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'

export const createClient = (cookieStore: ReturnType<typeof cookies>) => {
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options })
          } catch (error) {
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
          }
        },
      },
    }
  )
}
Russian Spaniel
I edited it again. the cookies-next was for the middleware section.
Sun bearOP
1 error left!
Russian Spaniel
replace the session with "" quotes
intellisense should popup
I don't know if it's user or what, can't remember 😅
Sun bearOP
Russian Spaniel
it should be data
Sun bearOP
No more errors!
Russian Spaniel
did you replace session={""} with session={data} ?
//app/[lang]/layout.tsx
import '../globals.css'
import { ThemeProvider } from "@/components/theme-provider"
import { PropsWithChildren } from 'react'
import TopNavigationBar from '@/components/TopNavigationBar'
import { SvgGradients } from '@/components/svg-gradient'
import fontVariables from '@/components/fonts'
import { createClient } from '@/utils/supabase/server'
import SessionProvider from '@/components/providers'

const defaultUrl = process.env.NODE_ENV === "production" ? `https://${process.env.VERCEL_URL}` : "https://www.jobsbringer.com"

export const metadata = {
  metadataBase: new URL(defaultUrl),
  title: 'Next.js and Supabase Starter Kit',
  description: 'The fastest way to build apps with Next.js and Supabase',
}

export default async function RootLayout({
  children,
}: PropsWithChildren) {

  const supabase = createClient();
  const { data } = await supabase.auth.getUser();

  return (
    <html lang="en" className={fontVariables} suppressHydrationWarning>
      <body className="font-roboto h-[100svh]">
        <SessionProvider session={data}>
          <SvgGradients />
          <ThemeProvider
            attribute="class"
            defaultTheme="system"
            enableSystem
          >
            {/* Page Background */}
            <main className="flex flex-col flex-grow items-center h-full">
              <TopNavigationBar />
              {children}
            </main>
          </ThemeProvider>
        </SessionProvider>
      </body>
    </html>
  );
}
Error-free!
Russian Spaniel
ok one last thing
to ensure that it's all working
inside the sessionProvider.tsx file
in the function
do a console.log(session)
just to ensure you are not seeing null
if you see an object in the server console.log
you know it's all working
Sun bearOP
Gotcha, let me go ahead and do that
@Russian Spaniel if you see an object in the server console.log
Sun bearOP
Russian Spaniel
what does the terminal say?
It says login
so it looks like you don't have a session?
which means null is the right answer
@Russian Spaniel what does the terminal say?
Sun bearOP
That's what the terminal says
By the way, it said that before adding the console.log you told me to add
I don't know if I'm logged in or not by the way
Russian Spaniel
is this hosted right now, on vercel?
Sun bearOP
It's local
Russian Spaniel
ok, well maybe try logging in?
Sun bearOP
Yup, will try right now
Russian Spaniel
and seeing if you still get user: null ?
@Russian Spaniel and seeing if you still get user: null ?
Sun bearOP
Here are the results
After logging in
Russian Spaniel
Awesome! Having an object means the session stuff is in there
Sun bearOP
Ignore the path console log it's from when I was having some issues with something and I forgot to remove it 😄
Russian Spaniel
ok, well you are pretty much there.
you'll have to figure the rest out yourself 😄
@Russian Spaniel Awesome! Having an object means the session stuff is in there
Sun bearOP
Wow, this took a while. Uh, so I have a question now. My entire website is now aware if a user is logged in or not right ?
Russian Spaniel
nope
well, zustand has the session
now it's up to you, to decide when to call zustand and pull out the session and then set the UI to whether a user is logged in or not
Sun bearOP
I see, and last question, well last 2 questions.
Russian Spaniel
sure
@Russian Spaniel sure
Sun bearOP
Question 1:

In my top navigation bar where I have my login button, I want it to turn into a button with user profile circle and their first and last name. How do I make it so that it knows when to do that ? As in what file do I have to import and what statement do I need to add ?

// TopNavigationBar.tsx
'use client';

import React, { useState } from 'react';
import { useTheme } from 'next-themes';
import { useRive } from '@rive-app/react-canvas';
import Link from 'next/link';
import LanguageSelector from './LanguageSelector';
import { Logo } from './Logo';

const TopNavigationBar: React.FC = () => {
  const { resolvedTheme: currentTheme } = useTheme();
  const [isHovered, setIsHovered] = useState(false);

//...Rest of my code

  return (
//.. Rest of my layout
  <Link href="/login"><span style={loginButtonStyle}>Log-in or Sign-up</span></Link>
//...Rest of my layout
  );
};

export default TopNavigationBar;
Russian Spaniel
You just need to import the zustand store now.
@Russian Spaniel You just need to import the zustand store now.
Sun bearOP
You mean this file ?
// utils/supabase/authState.js

import create from 'zustand';

const useUserSessionStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

export default useUserSessionStore;
Russian Spaniel
that's it and then pull out the user key
const { user } from useUserSessionStore();
something like that
Sun bearOP
Oh, I see, gotcha!
Russian Spaniel
user will have that object you saw in the console
you can do something like, user === null && <NotLoggedIn /> and user !== null && <LoggedIn />
Sun bearOP
Thank you very much!

And last question:

Could you please share how you have set up your middleware & route protection to work with what we've done ?

It will help me when talking to GPT as an example to protect my own routes.

You don't need to share your whole middleware, just enough so that I can see how it's done, that would help alot
I can share mine, it currently looks like this:
//middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { i18n } from '@/i18n.config' // Import your i18n configuration
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { match as matchLocale } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'

function getLocale(request: NextRequest): string {
  const negotiatorHeaders: Record<string, string> = {}
  request.headers.forEach((value, key) => (negotiatorHeaders[key] = value))

  const locales: ReadonlyArray<string> = i18n.locales;
  const languages = new Negotiator({ headers: negotiatorHeaders }).languages()

  const locale = matchLocale(languages, locales, i18n.defaultLocale)
  return locale
}

export async function middleware(request: NextRequest) {
  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return request.cookies.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          request.cookies.set({ name, value, ...options })
        },
        remove(name: string, options: CookieOptions) {
          request.cookies.set({ name, value: '', ...options })
        },
      },
    }
  )

  await supabase.auth.getSession()

  const pathname = request.nextUrl.pathname
  const pathnameIsMissingLocale = i18n.locales.every(
    locale => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  )

  if (pathnameIsMissingLocale) {
    const locale = getLocale(request)
    return NextResponse.redirect(
      new URL(
        `/${locale}${pathname.startsWith('/') ? '' : '/'}${pathname}`,
        request.url
      )
    )
  }

  return NextResponse.next()
}

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico|images|static).*)',
  ],
}
Russian Spaniel
Let me come back to you on that one, because I've got two middlewares right now and not yet unified them into 1.

So when I get it sorted. I'll paste it here.
I should do it sometime tomorrow.
@Russian Spaniel Let me come back to you on that one, because I've got two middlewares right now and not yet unified them into 1. So when I get it sorted. I'll paste it here.
Sun bearOP
Sure thing! I also want to mention that I have 2 middlewares, the one i pasted above which is the one at the root of my project and another one which was created by the supabase template which I'm not sure if my project is even using, I'll be leaving it here:

//utils/supabase/middleware.ts

import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { type NextRequest, NextResponse } from 'next/server'

export const createClient = (request: NextRequest) => {
  // Create an unmodified response
  let response = NextResponse.next({
    request: {
      headers: request.headers,
    },
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return request.cookies.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          // If the cookie is updated, update the cookies for the request and response
          request.cookies.set({
            name,
            value,
            ...options,
          })
          response = NextResponse.next({
            request: {
              headers: request.headers,
            },
          })
          response.cookies.set({
            name,
            value,
            ...options,
          })
        },
        remove(name: string, options: CookieOptions) {
          // If the cookie is removed, update the cookies for the request and response
          request.cookies.set({
            name,
            value: '',
            ...options,
          })
          response = NextResponse.next({
            request: {
              headers: request.headers,
            },
          })
          response.cookies.set({
            name,
            value: '',
            ...options,
          })
        },
      },
    }
  )

  return { supabase, response }
}
Sun bearOP
I'll be waiting for your message tomorrow or when you have some time. In the meantime I would like to thank from the bottom of my heart to everyone here that came to help me.

🙏🏻 ❣️
Russian Spaniel
This isn't tested, so don't shoot me. 😅

This is my middleware. You'll have to customise it for your own needs.

https://hastebin.com/share/itijaxigeq.php
Don't copy and paste, first of all. You need to solve the import of the top line: @acme/auth
Sun bearOP
One small thing @Russian Spaniel
I just noticed that Vs Code says this
// utils/supabase/authState.js

import create from 'zustand';

const useUserSessionStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

export default useUserSessionStore;
For one of the files we created
Do I need to delete the create and use the import it says ?
Russian Spaniel
I don't use zustand, so I don't know.
Sun bearOP
No worries then I will use GPT to help me 😁
This solved it.
Sun bearOP
@Russian Spaniel I have just noticed something.
2 of my files now return errors after we've updated the the server.ts file to how we have it now.:
So the server file was like this before:
//utils/supabase/server.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'

export const createClient = (cookieStore: ReturnType<typeof cookies>) => {
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options })
          } catch (error) {
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
          }
        },
      },
    }
  )
}
And now we have it like this:
import {cookies} from "next/headers";
import {createServerClient} from "@supabase/ssr";

export function createClient(debug?: boolean) {
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      global: {
        fetch: (...args) => {
          debug !== undefined && debug && console.log(args);

          return fetch(...args);
        }
      },
      cookies: {
        get(name: string) {
          return cookies().get(name)?.value;      
        },
        set(name: string, value, options) {
          cookies().set(name, value, options);
        },
        remove(name: string, options) {
          cookies().set(name, "", options);
        },       
      },
    }
  );  
};
But, this file:
// app/[lang]/signup/submit/route.ts
import { cookies } from 'next/headers';
import { createClient } from '@/utilities/supabase/server';
import type { SignupData } from '@/utilities/types';
import { NextResponse } from 'next/server';

export const POST = async (req: Request) => {
    console.log(`Request received: POST ${req.url}`);
    
    const body = await req.json();
    console.log('Parsed request body:', body);

    const { personalDetails, accountType, accountDetails }: SignupData = body;
    const cookieStore = cookies();
    const supabase = createClient(cookieStore);

    const { data: signUpData, error: signUpError } = await supabase.auth.signUp({
        email: accountDetails.email,
        password: accountDetails.password,
    });

    if (signUpError) {
        console.error('Supabase signUp error:', signUpError);
        return new NextResponse(JSON.stringify({ error: signUpError.message }), { status: 500 });
    }

    if (!signUpData.user) {
        console.error('User was not created.');
        return new NextResponse(JSON.stringify({ error: 'User was not created.' }), { status: 500 });
    }

    const userData = {
        user_id: signUpData.user.id,
        firstName: personalDetails.firstName,
        middleName: personalDetails.middleName,
        lastName: personalDetails.lastName,
        country: personalDetails.country,
        dateOfBirth: personalDetails.dateOfBirth,
        accountType: accountType.userType,
        email: accountDetails.email,
        subscriptionType: null,
    };
    const { data: insertData, error: insertError } = await supabase.from('Unconfirmed Users').insert([userData]);

    if (insertError) {
        console.error('Error inserting user data into Unconfirmed Users:', insertError);
        console.error('Detailed error:', insertError.message, insertError.code);
        return new NextResponse(JSON.stringify({ error: insertError.message }), { status: 500 });
    }

    console.log('User data inserted successfully into Unconfirmed Users:', insertData);
    return new NextResponse(JSON.stringify({ data: { user: signUpData.user, additionalData: insertData } }), { status: 200 });
};
Gives this error now
And I have 1 other file that gives the same error
Russian Spaniel
yeah the param was removed
can you not see it by looking at the code?
Sun bearOP
So how can I fix it ?
Russian Spaniel
you remove the cookiestore
the param has been removed
Pacific herring
just update it where its giving the error
Sun bearOP
But wasn't the cookiestore required ?
Russian Spaniel
your old code:
export const createClient = (cookieStore: ReturnType<typeof cookies>) => {

my code:
export function createClient(debug?: boolean) {

can you not see the params are different?
Pacific herring
ehm i dont know?
if you removed it from the function then you need to remove it everywhere in the files
Russian Spaniel
const supabase = createClient();

that's all you need.
If you read the code carefully, this should be apparent.
Sun bearOP
Thank you. I have done that and it fixed both errors, also removed the cookiestore constant since it's no longer needed I suppose.
Sun bearOP
// context/AuthContext.js
import { createContext, useContext, useState, useEffect } from 'react';
import { supabase } from '../utils/supabaseClient';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    // Recover session if page is refreshed
    const session = supabase.auth.session();
    setUser(session?.user ?? null);

    // Listen for changes on authentication state (sign in or sign out)
    const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
      setUser(session?.user ?? null);
    });

    // Cleanup the listener when component is unmounted
    return () => {
      authListener?.unsubscribe();
    };
  }, []);

  const logout = async () => {
    await supabase.auth.signOut(); // Sign out with Supabase
    setUser(null); // Update the user state to null
  };

  return (
    <AuthContext.Provider value={{ user, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  return useContext(AuthContext);
};


Is this file being needed anymore in my project or can I archive it ?

I just need to know this for my documentation.
Russian Spaniel
or you can delete it since it's not needed
you aren't storing the user session in a context
it's in zustand
Sun bearOP
Yeah well hold on cause I was testing my website (didn't test it since removing the cookiestore) and now it gives an error when loading
@Russian Spaniel or you can delete it since it's not needed
Sun bearOP
Thank you, I will simply just delete it then.
Russian Spaniel
like i said before. my middleware is not tested. although what i've given you works fine for me. but i'll let you know tomorrow.
Sun bearOP
Oh, okay then.
I haven't modified my middleware yet by the way, the error seems to be coming from the server.ts file.
// utilities/supabase/server.ts

// this can be used inside server components

import {cookies} from "next/headers";
import {createServerClient} from "@supabase/ssr";

export function createClient(debug?: boolean) {
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      global: {
        fetch: (...args) => {
          debug !== undefined && debug && console.log(args);

          return fetch(...args);
        }
      },
      cookies: {
        get(name: string) {
          // return cookie with the 'name' here    
          return cookies().get(name)?.value;      
        },
        set(name: string, value, options) {
          // set cookie
          cookies().set(name, value, options);
        },
        remove(name: string, options) {
          // remove cookie
          cookies().set(name, "", options);
        },       
      },
    }
  );  
};
Sun bearOP
//app/[lang]/page.tsx

import { Locale } from '@/i18n.config' // Import localization configuration
import { getDictionary } from '../translation/dictionary'; // Import dictionary function 
import DeployButton from '../../components/DeployButton'
import AuthButton from '../../components/AuthButton'
import { createClient } from '@/utilities/supabase/server'
import ConnectSupabaseSteps from '@/components/ConnectSupabaseSteps'
import SignUpUserSteps from '@/components/SignUpUserSteps'
import Header from '@/components/Header'
import { cookies } from 'next/headers'

export default async function Index({
  params: { lang } // Accept language parameter
}: {
  params: { lang: Locale } // Add language parameter to the function
}) {
  const cookieStore = cookies()
  const { page } = await getDictionary(lang) // Fetch localized content

  const canInitSupabaseClient = () => {
    // This function is just for the interactive tutorial.
    // Feel free to remove it once you have Supabase connected.
    try {
      createClient
      return true
    } catch (e) {
      return false
    }
  }

  const isSupabaseConnected = canInitSupabaseClient()

  return (
    <div className="flex-1 w-full flex flex-col gap-20 items-center">
      <nav className="w-full flex justify-center border-b border-b-foreground/10 h-16">
        <div className="w-full max-w-4xl flex justify-between items-center p-3 text-sm">
          <DeployButton />
          {isSupabaseConnected && <AuthButton />}
        </div>
      </nav>

      <div className="animate-in flex-1 flex flex-col gap-20 opacity-0 max-w-4xl px-3">
        <Header />
        <main className="flex-1 flex flex-col gap-6">
          <h2 className="font-bold text-4xl mb-4">{page.home.title}</h2> {/* Use localized title */}
          <p className="text-gray-500">{page.home.description}</p> {/* Use localized description */}
          {isSupabaseConnected ? <SignUpUserSteps /> : <ConnectSupabaseSteps />}
        </main>
      </div>
      <footer className="w-full border-t border-t-foreground/10 p-8 flex justify-center text-center text-xs">
        <p>
          Powered by{' '}
          <a
            href="https://supabase.com/?utm_source=create-next-app&utm_medium=template&utm_term=nextjs"
            target="_blank"
            className="font-bold hover:underline"
            rel="noreferrer"
          >
            Supabase
          </a>
        </p>
      </footer>
    </div>
  )
}

And here in my page.tsx file there is a const named cookiestore, I suppose I need to delete that but my question is do I need to replace it with another const or do I just delete it and leave the file as it is?
Russian Spaniel
I haven't modified my middleware yet by the way,
if you haven't updated to the middleware I gave you this morning. then that's why you are getting the error.
const cookieStore = cookies()
  const { page } = await getDictionary(lang) // Fetch localized content

  const canInitSupabaseClient = () => {
    // This function is just for the interactive tutorial.
    // Feel free to remove it once you have Supabase connected.
    try {
      createClient
      return true
    } catch (e) {
      return false
    }
  }

  const isSupabaseConnected = canInitSupabaseClient()


why do you even have this code?
you have a zustand store now
look, I'm not trying to be mean because I'm sincerely trying to help you.

but do you understand your code or are you basically just copying and pasting from GPT-4 ?
Russian Spaniel
I would really advise splitting up your time. learn React. Freecode camp does an excellent course. It's 12 hours, but you'd be in a different place in a months time. https://www.youtube.com/watch?v=bMknfKXIFA8
Russian Spaniel
I don't know anything about localization. Best start a new thread.
Ok, well with the middleware done. My job is complete. You should have a working auth system now.
Sun bearOP
Yup, I've updated my middleware and the website now seems to work again.
Russian Spaniel
I tested the middleware throughout today and just now to ensure that the token refreshes and there are no serverside errors (like the one you got just now). It all works well.
Sun bearOP
Can you have a look at how mine looks to tell me if it's okay ?
Russian Spaniel
I'm essentially using the same supabase code you are.
@Russian Spaniel I don't know anything about localization. Best start a new thread.
Sun bearOP
I don't need help with that, I have already implemented it following a tutorial. I was just saying that cause you asked me to delete something which is used in my localization.
Russian Spaniel
const { page } = await getDictionary(lang) // Fetch localized content

yeah exclude that
@Russian Spaniel I tested the middleware throughout today and just now to ensure that the token refreshes and there are no serverside errors (like the one you got just now). It all works well.
Sun bearOP
Okay, I would like to post how my middleware looks at the moment since right now it's sort of a hybrid between what I had previously and your version. My website works now but I just want to make sure that my middleware is set-up properly.

Are you okay with taking a look at it ?
Russian Spaniel
sure
Sun bearOP
Thank you very much, give me a moment, I will have to paste it in 2 separate messages.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

import { i18n } from '@/i18n.config';
import { createServerClient, type CookieOptions } from '@supabase/ssr';
import Negotiator from 'negotiator';
import { match as matchLocale } from '@formatjs/intl-localematcher';

// Function to determine the user's locale
function getLocale(request: NextRequest): string {
  const locales: ReadonlyArray<string> = i18n.locales;

  // Transform the headers to match the expected format for Negotiator
  const headers = Object.fromEntries(request.headers.entries());

  const negotiator = new Negotiator({ headers: headers });
  const languages = negotiator.languages();
  const locale = matchLocale(languages, locales, i18n.defaultLocale);
  return locale;
}

// Middleware function remains the same
export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  let response = NextResponse.next();

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return request.cookies.get(name)?.value;
   },
        set(name: string, value: string, options: CookieOptions) {
          request.cookies.set({ name, value, ...options });
          response = NextResponse.next();
          response.cookies.set({ name, value, ...options });
        },
        remove(name: string, options: CookieOptions) {
          request.cookies.set({ name, value: '', ...options });
          response = NextResponse.next();
          response.cookies.set({ name, value: '', ...options });
        },
      },
    }
  );

  const sessionResult = await supabase.auth.getSession();
  const user = sessionResult.data?.session?.user; // Adjusted based on the expected structure

  if (["/login", "/logout", "/signup", "/forgotPassword"].includes(pathname)) {
    if (user) {
      return NextResponse.redirect(new URL("/", request.url));
    } else if (!user && pathname !== '/login') {
      return NextResponse.redirect(new URL("/login", request.url));
    }
  }

  const pathnameIsMissingLocale = i18n.locales.every(
    locale => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  );

  if (pathnameIsMissingLocale) {
    const locale = getLocale(request);
    return NextResponse.redirect(
      new URL(`/${locale}${pathname}`, request.url)
    );
  }

  return response;
}

export const config = {
  matcher: [
    "/((?!api|_next/static|_next/image|favicon.ico|sw.js|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
  ],
}
Russian Spaniel
replace
const sessionResult = await supabase.auth.getSession();
  const user = sessionResult.data?.session?.user; // Adjusted based on the expected structure


with
const {
    data: { user },
  } = await supabase.auth.getUser();
@Russian Spaniel replace js const sessionResult = await supabase.auth.getSession(); const user = sessionResult.data?.session?.user; // Adjusted based on the expected structure with js const { data: { user }, } = await supabase.auth.getUser();
Sun bearOP
Thank you very much, it works without errors and my website still loads! I also tested the localization and it still works. 😃
Russian Spaniel
Also:

else if (!user && pathname !== '/login') {
      return NextResponse.redirect(new URL("/login", request.url));
    }


I'm not sure what that does exactly. you'll have to find out in testing
I mean, it's if user is false and pathname does not equal /login, then redirect to /login

but ... 🤷‍♂️
again find out when you test 😄
Sun bearOP
Gpt says it does this
Russian Spaniel
When I mean test. I mean you actually hit the endpoints of the site and see it in action. Rather than that GPT says
You've got to test your code when there is a session and without a session, so you can adjust your endpoints in the middleware.
Sun bearOP
Oh, I see what you mean. Hmm, I don't have a log-out button yet but I will try in icognito.
Russian Spaniel
I already gave you the code for that. 😄
@Russian Spaniel I already gave you the code for that. 😄
Sun bearOP
Thank. you, it's just that haven't implemented a sign-out button / functionality yet. I have tested my website in icognito and there is no redirection happening. I can access whatever page I want. I also checked the user object and it looks to be null which from what I understand it means I'm not signed in so in theory I should be redirected to the login page but I'm not which I guess it means that code snippet we've talked about doesn't work.
Russian Spaniel
if the user is null, then there is no session
your code is different from mine, in the way it handles the redirection
Sun bearOP
I see, no worries, I'll try and see if I can solve this with Gpt. Route protection is a separated topic anyway so in case I can't solve it by myself I'll open a new thread.

Again, thank you very much for your help ❤️
@Pacific herring are you using nextauth?
Russian Spaniel
no he's using supabase
Sun bearOP
I'm using supabase and I'm having a hard time refactoring my page.tsx with gpt to use the zustand store like Paul said.
I keep getting errors in the code after modifying it
//app/[lang]/page.tsx

import { Locale } from '@/i18n.config' // Import localization configuration
import { getDictionary } from '../translation/dictionary'; // Import dictionary function 
import DeployButton from '../../components/DeployButton'
import AuthButton from '../../components/AuthButton'
import { createClient } from '@/utilities/supabase/server'
import ConnectSupabaseSteps from '@/components/ConnectSupabaseSteps'
import SignUpUserSteps from '@/components/SignUpUserSteps'
import Header from '@/components/Header'
import { cookies } from 'next/headers'

export default async function Index({
  params: { lang } // Accept language parameter
}: {
  params: { lang: Locale } // Add language parameter to the function
}) {

  const { page } = await getDictionary(lang) // Fetch localized content

  return (
    <div className="flex-1 w-full flex flex-col gap-20 items-center">
      <nav className="w-full flex justify-center border-b border-b-foreground/10 h-16">
        <div className="w-full max-w-4xl flex justify-between items-center p-3 text-sm">
          <DeployButton />
        </div>
      </nav>

      <div className="animate-in flex-1 flex flex-col gap-20 opacity-0 max-w-4xl px-3">
        <Header />
        <main className="flex-1 flex flex-col gap-6">
          <h2 className="font-bold text-4xl mb-4">{page.home.title}</h2> {/* Use localized title */}
          <p className="text-gray-500">{page.home.description}</p> {/* Use localized description */}
        </main>
      </div>

      <footer className="w-full border-t border-t-foreground/10 p-8 flex justify-center text-center text-xs">
        <p>
          Powered by{' '}
          <a
            href="https://supabase.com/?utm_source=create-next-app&utm_medium=template&utm_term=nextjs"
            target="_blank"
            className="font-bold hover:underline"
            rel="noreferrer"
          >
            Supabase
          </a>
        </p>
      </footer>
    </div>
  )
}
I ended up just removing that function and its related components altogether and the website works but I'm not sure if I need to add something from zustand in it or not.
@Russian Spaniel Also: js else if (!user && pathname !== '/login') { return NextResponse.redirect(new URL("/login", request.url)); } I'm not sure what that does exactly. you'll have to find out in testing
Sun bearOP
I have found out what that is doing. It's for when the user is actually logged in, and it prevents the user from accessing the log-in page.
Very cool 👌
Russian Spaniel
you just need to import the zustand store, like you did before.
@Russian Spaniel you just need to import the zustand store, like you did before.
Sun bearOP
//app/[lang]/page.tsx

import { Locale } from '@/i18n.config'
import { getDictionary } from '../translation/dictionary';
import DeployButton from '../../components/DeployButton'
import { createClient } from '@/utilities/supabase/server'
import Header from '@/components/Header'
import SessionProvider from '@/components/providers'

export default async function Index({
  params: { lang } // Accept language parameter
}: {
  params: { lang: Locale } // Add language parameter to the function
}) {
  const supabase = createClient();
  const { data } = await supabase.auth.getUser();

  const { page } = await getDictionary(lang)
  return (
    <SessionProvider session={data}> {/* Wrap content with SessionProvider */}
      <div className="flex-1 w-full flex flex-col gap-20 items-center">
        <nav className="w-full flex justify-center border-b border-b-foreground/10 h-16">
          <div className="w-full max-w-4xl flex justify-between items-center p-3 text-sm">
            <DeployButton />
          </div>
        </nav>

        <div className="animate-in flex-1 flex flex-col gap-20 opacity-0 max-w-4xl px-3">
          <Header />
          <main className="flex-1 flex flex-col gap-6">
            <h2 className="font-bold text-4xl mb-4">{page.home.title}</h2> {/* Use localized title */}
            <p className="text-gray-500">{page.home.description}</p> {/* Use localized description */}
          </main>
        </div>

        <footer className="w-full border-t border-t-foreground/10 p-8 flex justify-center text-center text-xs">
          <p>
            Powered by{' '}
            <a
              href="https://supabase.com/?utm_source=create-next-app&utm_medium=template&utm_term=nextjs"
              target="_blank"
              className="font-bold hover:underline"
              rel="noreferrer"
            >
              Supabase
            </a>
          </p>
        </footer>
      </div>
    </SessionProvider>
  )
}

Like this ?
Russian Spaniel
no
there's too many messages here now, the zustand code you had that took the user... go find it
Sun bearOP
// utilities/zustand/authState.js

import { create } from 'zustand';

const useUserSessionStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

export default useUserSessionStore;

This one ?
Russian Spaniel
no that's setting the store
remember when I said, console.log out the user
that one
@Russian Spaniel remember when I said, console.log out the user
Sun bearOP
// components/providers.tsx
"use client";
import useUserSessionStore from "../utilities/zustand/authState.js";
import { useEffect } from "react";

interface SessionProviderProps {
  session: any;
  children: React.ReactNode;
}

export default function SessionProvider({ session, children }: SessionProviderProps) {
  const { setUser } = useUserSessionStore();

  useEffect(() => {
    console.log(session);  // Log the session object to verify it's not null
    setUser(session);
  }, [session, setUser]);

  return <>{children}</>;
}

It has to be my providers file then, here is where I have that log.
Russian Spaniel
ok, so then in your other file you basically just need.

import useUserSessionStore from "../utilities/zustand/authState.js";
const { user } = useUserSessionStore();

console.log(user);
that's how you get the user information from the zustand store
you don't need to use supabase any more
Russian Spaniel
What's in red?
Sun bearOP
There is nothing in red in the code
Pacific herring
apparently you cant use useusersessionstore
@Sun bear It doesn't work
Russian Spaniel
Add the "use client"
Sun bearOP
But that would make my root page a client page, I want to use SSR.
Russian Spaniel
Why are you adding it to layout?
You should be adding the zustand store, to pages where you need to show the components which show a login or not
Sun bearOP
Okay so on my root page.tsx I don't need to add anything related to zustand or supabase right ?
Russian Spaniel
does your root page.tsx need to show whether a user is auth or not via a component?
@Russian Spaniel does your root page.tsx need to show whether a user is auth or not via a component?
Sun bearOP
I don't think so, I have a top navigation bar (which is a separate component) in which I do indeed show a log-in button or the user name and profile picture if they are logged in.
Russian Spaniel
normally, you have something like this:

"use client";
import useUserSessionStore from "../utilities/zustand/authState.js";

export function ShowLoggedIn() { 
  const { user } = useUserSessionStore();
  
  return (
    <>
      {user === null && "Little timmy not logged in"}
      {user !== null && "Little timmy is logged in"}
    </>
  )
}
which you then import to the page component
so you have something like this for the login/logout button
Sun bearOP
So that means that all the components in which I will be using that would need to be client components else this will not work out :thinq:
Russian Spaniel
no, it itself is a client component
so you can import it into a server component
Sun bearOP
But it did not work in my root page.tsx which was not a client component.
Russian Spaniel
"use client" === client component
You don't have the necessary knowledge to debug this. It's going to be difficult.
Sun bearOP
I have updated my root page like this, it no longer has any Supabase or Zustand related code:
//app/[lang]/page.tsx

import { Locale } from '@/i18n.config'
import { getDictionary } from '../translation/dictionary';

export default async function Index({
  params: { lang }
}: {
  params: { lang: Locale }
}) {

  const { page } = await getDictionary(lang)

  return (
    <div className="flex-1 w-full h-full flex justify-center items-center">
      <main className="animate-in opacity-0 flex justify-center items-center h-full w-full">
        <h2 className="font-bold text-4xl text-center m-0">{page.home.title}</h2>
      </main>

      <footer className="absolute bottom-0 w-full p-8 flex justify-center text-center text-xs">
      </footer>
    </div>
  )
}
Russian Spaniel
why don't you make a new file, with the code I created above. and import it to your root page.tsx and add it as a component
then you can get a feel for what it does, when a user is logged in and not logged in
@Russian Spaniel why don't you make a new file, with the code I created above. and import it to your root page.tsx and add it as a component
Sun bearOP
Because that's what I will do next but in my top navigation bar instead since the next step I want to take care of is to add a sign-out button when the user is logged in 😁

And that's where the code snippet you provided me with will be very helpful.