Unlock the full potential of Next.js 13 with Supabase: Discover the best practices for setting up a user context in your application

I am currently developing an app using Next.js 13 and Supabase for the backend. I have been facing a challenge in determining the most effective way to create a context or provider for the logged-in user.

The process of retrieving the user from Supabase involves:

  1. Signing in with an OAuth Provider.
  2. Extracting the user ID from the session through the Supabase onAuthStateChanged hook.
  3. Fetching the complete user object from the Supabase DB using the obtained user ID.

In my layout, I have implemented a Supabase listener that effectively handles authentication state changes and updates the current session.

Initially, I attempted to execute the fetchUser function within the onAuthStateChanged hook, but encountered issues related to late update hydration errors.

This is how the application looks based on the examples provided:

// layout.tsx
export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const supabase = createServerComponentSupabaseClient<Database>({
    headers,
    cookies,
  });
  const {
    data: { session },
  } = await supabase.auth.getSession();

  return (
    <html>
      <head />
      <body>
        <NavMenu session={session} />
        <SupabaseListener accessToken={session?.access_token} />
        {children}
      </body>
    </html>
  );
}
// supabase-listener.tsx
// sourced directly from the supabase-auth-helpers library.

"use client";

import { useRouter } from "next/navigation";
import { useEffect } from "react";
import supabase from "../lib/supabase/supabase-browser";

export default function SupabaseListener({
  accessToken,
}: {
  accessToken?: string;
}) {
  const router = useRouter();

  useEffect(() => {
    supabase.auth.onAuthStateChange(async (event, session) => {
      if (session?.access_token !== accessToken) {
        router.refresh();
      }
    });
  }, [accessToken, router]);

  return null;
}

Essentially, I plan to incorporate a LoggedInUserProvider around the root layout, trigger the fetch user call during the initial page load, and set the state within the provider for the current logged-in user.

Other methods I explored involved executing the fetch user call from the root layout and utilizing a LoggedInUserListener client component that accepts the user as a property and updates the state only if the profile exists. However, this approach resulted in incorrect state setting errors.

Your assistance is greatly appreciated.

Answer №1

Take a look at this PR for a great example of how to structure your application and implement a provider for sharing a single Supabase client instance client-side, along with the session from the server 👍

If you adopt a similar approach, make sure to place your additional query for the full user record right after fetching the session in

examples/nextjs-server-components/app/layout.tsx
. You can then pass this as a prop to the <SupabaseProvider /> and access it throughout the application using the context's value prop.

Answer №2

While implementing the auth-helpers example provided, I am encountering an issue where my user details from the context provider are returning as null. Is there a problem with the code below or should I incorporate some isLoading logic to retrieve the data more effectively?

I would also like to clarify whether the SupabaseProvider in the root layout cascades down to all child layout components.

'use client';

import type { Session } from '@supabase/auth-helpers-nextjs';
import { createContext, useContext, useState, useEffect } from 'react';
import type { TypedSupabaseClient } from 'app/layout';
import { createBrowserClient } from 'utils/supabase-client';
import { UserDetails, CompanyDetails } from 'models/types';

type MaybeSession = Session | null;

type SupabaseContext = {
  supabase: TypedSupabaseClient;
  session: MaybeSession;
  userDetails: UserDetails | null;
  isLoading: boolean;
};

// @ts-ignore
const Context = createContext<SupabaseContext>();

//TODO get stripe subscription data
export default function SupabaseProvider({
  children,
  session
}: {
  children: React.ReactNode;
  session: MaybeSession;
}) {
  const [supabase] = useState(() => createBrowserClient());
  const [userDetails, setUserDetails] = useState<UserDetails | null>(null);
  const [isLoading, setLoading] = useState(false);
  // Hydrate user context and company data for a user
  useEffect(() => {
    const fetchUserDetails = async () => {
      if (session && session.user) {
        setLoading(true);
        const { data } = await supabase
          .from('users')
          .select('*, organizations (*)')
          .eq('id', session.user.id)
          .single();
        //TODO fix types
        setUserDetails(data as any);
        setLoading(false);
      }
    };
    if (session) {
      fetchUserDetails();
    }
  }, [session, supabase]);
  return (
    <Context.Provider value={{ supabase, session, userDetails, isLoading }}>
      <>{children}</>
    </Context.Provider>
  );
}

export const useSupabase = () => useContext(Context);


Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing React refs for interactive tab panels

Currently, I am utilizing React along with material-ui and material-table in my project. One issue I am facing is with a closable tab panel where the corresponding panel components are not unmounted when tabs are switched. Instead, they are just hidden wi ...

What could be causing my Next.js layout to re-render?

I currently have a basic root layout set up in Nextjs (app router) that displays a navigation bar and the child components underneath it. ROOT LAYOUT import "./globals.css"; import type { Metadata } from "next"; import { Inter } from & ...

The provided Material-UI Fade component contains multiple children, which is not supported by 'ReactElement<any, any> | undefined'

I'm struggling to implement a Material UI <Fade> component in my code. Unfortunately, I keep encountering the following error message and as someone who is still learning TypeScript, I am unsure of how to resolve it. Error: Expected ReactElement ...

I am experiencing an issue where emojis are not displaying properly on Facebook, Instagram, and LinkedIn when I share posts from my

I've developed a unique social media management application that enables users to automatically post on their social profiles using the Facebook, Instagram, and LinkedIn APIs. Everything is functioning well, except for one issue - my Emojis are not di ...

Verifying user authorization for Microphone access prior to triggering event in React application

I have created a React component featuring a microphone button that operates as follows: OnMouseDown => Initiates audio recording by the user OnMouseUp => Ceases audio recording To elaborate, while the button is pressed down, the user can continue ...

Is there a way to restrict my input to only 10 numbers without allowing any characters?

Is there a way to restrict the phone number input to only allow 10 numbers and exclude any other characters? Currently, setting the maxLength attribute limits the characters to 10 but allows additional characters as well. If I change the type attribute to ...

bypassing files in mongodb for paging purposes

I need to retrieve specific documents based on the page count parameter in my GET request. For instance, when I send a GET request to http://localhost:3001/posts?page=2, I want to receive 10 documents per page starting from document 10 to 20. router/posts ...

How to implement loading an external script upon a page component being loaded in NextJS

I recently transferred an outdated website to Nextjs and I am having trouble getting the scripts to load consistently every time a page component is loaded. When navigating between pages using next/link component, the scripts only run the first time the ...

positioning of multiple buttons in a customized header for mui-datatables

I'm currently using mui-datatables in my app and have customized the table toolbar to include additional buttons. However, I've encountered an issue where adding a second button causes it to be displayed below the first one, despite there being e ...

The command 'yar' is not a valid cmdlet that can be recognized

After running the installation code for Yarn, I received this message. How can I properly install Yarn on my project using Windows 11? npm install -g yarn changed 1 package, and audited 2 packages in 922ms found 0 vulnerabilities (yar : The term 'ya ...

Error in parsing: Looking for the correct JSX closing tag for <Route>?

Having trouble linking my car page so that it redirects to the correct location. I am encountering an error message indicating a JSX closing tag for route, even though all tags appear to be properly closed. Can't figure out why this error keeps occurr ...

What are some ways to avoid the use of underline and slash symbols in material-ui/pickers?

Is there a way to remove the underline and slash characters that separate day, month, and year in the material ui pickers for version mui version 4? <KeyboardDatePicker margin="normal" id="date-picker-dialog" label="Dat ...

Prevent the need to go through the keycloak callback process each time the page is

I have integrated keycloak as an identity provider into my React application. I successfully added the keycloak react dependency via npm. Below are the versions of the keycloak react npm modules on which my application depends : "@react-keycloak/web ...

Icon for closing Mui Snackbar

I am facing an issue with my notification component that uses the mui snackbar to display alerts. I want to display multiple notifications stacked vertically, but when I try to close one notification using the "Close" icon, it ends up closing both that o ...

Issues with React Native imports not functioning properly following recent upgrade

Hey there, I’ve been tasked with updating an old React-Native iOS project from version 0.25.1 to 0.48.0. However, I’m encountering several compiler issues and struggling to navigate through the code updates. The project includes an index.ios.js file s ...

Tips on preventing the first letter from being capitalized in an input field

Currently, I am developing a React web application primarily used on mobile devices. We have an input field and our goal is to ensure that the first letter entered is not automatically capitalized. The input field can still contain capital letters, but ...

The Mechanics of Running "npm start" in create-react-apps

Can you explain what activity occurs behind the scenes when the npm start command is executed? Does it create a web server to facilitate communication between your browser and the application? ...

Is Nextjs the best choice for developing the frontend code exclusively?

When deciding whether to use Next.js or plain React for my front-end development, should I consider that a back-end already exists? If I am not planning to write a new back-end, which option would be better suited for the project? ...

Is there a correlation between eliminating unnecessary imports and the size of the bundle as well as the speed of the build process

I am working on a Reactjs application built with Create React app, and I often encounter warnings during the startup and build process indicating that there are unused variables or imports in my Components. ./src/components/home/Header.js Line 10: & ...

Encountered an issue trying to access the 'secret' property as it is undefined at the function unstable_getServerSession

_app.js import "bootstrap/dist/css/bootstrap.min.css"; import { SessionProvider } from "next-auth/react"; import Layout from "../components/Layout"; import "../styles/globals.css"; function MyApp({ Component, pageP ...