DEV Community

frontcodelover
frontcodelover

Posted on

2

Securing Client-Side Routes in Next.js with Supabase

Ensuring that only authenticated users can access certain pages or features is crucial in web applications. This article explains how to protect routes in Next.js using Supabase authentication with a fully client-side approach. We'll avoid middleware and focus on simplicity and dynamic updates. You Shall not pass !


Image description

Why Use Client-Side Route Protection?

  1. Simplified Implementation: A client-side solution enforces authentication checks directly in React components, no middleware required.
  2. Dynamic Auth State Handling: React instantly to user authentication changes for a seamless experience.
  3. Flexibility: Perfect for Single Page Applications (SPAs) or dynamic rendering.

Setting Up Supabase

First, initialize Supabase in a supabase.js file:

'use client';

import { createClient } from '@supabase/supabase-js';

if (!process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) {
  throw new Error('Missing Supabase environment variables');
}

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
Enter fullscreen mode Exit fullscreen mode

This ensures the Supabase client is available for authentication operations.

Creating the PrivateRoute Component

The PrivateRoute component ensures only authenticated users can access the wrapped content. It checks for a valid session on mount and listens for authentication state changes.

'use client';

import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { supabase } from '@/lib/supabase';

export default function PrivateRoute({ children }: { children: React.ReactNode }) {
  const [isLoading, setIsLoading] = useState(true);
  const router = useRouter();

  useEffect(() => {
    const checkAuth = async () => {
      try {
        const { data: { session } } = await supabase.auth.getSession();
        if (!session) {
          router.replace('/signin');
          return;
        }
        setIsLoading(false);
      } catch (error) {
        console.error('Error verifying authentication:', error);
        router.replace('/signin');
      }
    };

    checkAuth();

    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange((_event, session) => {
      if (!session) {
        router.replace('/signin');
      }
    });

    return () => {
      if (subscription) {
        subscription.unsubscribe();
      }
    };
  }, [router]);

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return <>{children}</>;
}
Enter fullscreen mode Exit fullscreen mode

Key Features

  • Session Validation: Checks for a valid session on mount using supabase.auth.getSession().
  • Auth State Listener: Automatically redirects users who log out or whose session expires.
  • Graceful Loading: Displays a loading state while verifying authentication.

Applying the PrivateRoute Wrapper

To use PrivateRoute, wrap the protected content in your layout or pages.

Creating a Layout Component

A layout ensures that the authentication check is applied to all nested pages:

'use client';

import PrivateRoute from '@/components/PrivateRoute';

export default function PrivateLayout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <PrivateRoute>
        {children}
      </PrivateRoute>
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

How It Works

  1. Initial Check: The PrivateRoute component checks for a valid session on mount.
  2. Real-Time Updates: The onAuthStateChange listener redirects users who log out while on a protected page.
  3. Reusable Structure: The layout structure simplifies extending protection to other app sections.

Benefits of This Approach

  • Real-Time Protection: Auth state changes are instantly reflected.
  • Component-Based: No need for additional middleware or server-side setup.
  • Client-Focused: Ideal for SPAs or apps relying on client-side rendering.

Conclusion

Using a client-side approach with Next.js and Supabase, you can implement robust route protection while maintaining a dynamic user experience. This method leverages Supabase's session management and Next.js's client components to enforce access control effectively. Give it a try and keep your app secure! πŸš€

Follow me on
X (twitter)
Linkedin

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series πŸ“Ί

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series πŸ‘€

Watch the Youtube series

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay