Supabase data loads only after a refresh

The page is empty until you reload? The first query likely ran before Supabase restored the session. Wait for INITIAL_SESSION, then load the rows.

Reviewed October 17, 2026

What it means in plain English

The table has rows, and a reload shows them, but the first paint does not. Supabase Auth keeps the session in storage and reads it back asynchronously. A query that runs before that read is a signed-out request. Row Level Security then returns no rows for a policy that requires a signed-in user, with no error.

Likely causes

  • The page queries the table in the same moment it creates the Supabase client, before getSession() resolves.
  • The app never listens for INITIAL_SESSION. Supabase's auth listener uses that event for the session it loaded at startup.
  • The access token was already expired. Access tokens usually last 5 minutes to 1 hour, and the default is 1 hour. The client refreshes ahead of time and emits TOKEN_REFRESHED. A query that used the old token, or that ran with no token, stays empty until something asks again.
  • getSession() loaded a cookie-based session on the server. Supabase says that storage may not be authentic and tells you to use getUser() in that case.

How to fix it, step by step

  1. Load the user's rows only after the client has a session. getSession() returns the session and refreshes it if needed. It returns null when no session is stored.
  2. Subscribe to auth changes as soon as you create the client. Handle INITIAL_SESSION before the first query, and run the query again on TOKEN_REFRESHED.
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
  if ((event === 'INITIAL_SESSION' || event === 'TOKEN_REFRESHED' || event === 'SIGNED_IN') && session) {
    loadRows();
  }
  if (event === 'SIGNED_OUT') {
    showSignedOut();
  }
});
  1. In a Next.js server app, follow Supabase's server guide: middleware calls getUser() so one refresh writes the new cookies. Don't also refresh the same token in the page. A second refresh can end the session. See JWT expired.
  2. If the user is signed out, an empty list is the policy working. See Supabase returns an empty array.
  3. Paste this to the AI tool that edits the app:
The Supabase query on first load returns no rows until I refresh the page. Wait for supabase.auth.onAuthStateChange. When the event is INITIAL_SESSION, TOKEN_REFRESHED, or SIGNED_IN and session is set, then query the table. Do not query in the same instant as createClient. On the server, use getUser() in middleware once, and do not refresh the same session again in the page.

How to check it worked

  • Open the page signed in, without reloading. The rows appear on the first paint.
  • In the Network tab, the data request that returns rows sends an Authorization header. The one that returns [] does not.
  • Stay signed in past the access-token expiry. The next load still returns rows after TOKEN_REFRESHED.

Sources

More Supabase auth and email fixes

Keep going on your own.

The guides stay free. A deep leak report is $15. Fix packs are $24, or $49 for all three. There is no done-for-you repair.

See products