Firebase Authentication in Next.js
javascript
web dev
next js
Firebase
authentication
Next.js
souravvmishra
tech
education
personal
Manyblogs
Manyblogs

Firebase Authentication in Next.js Manyblogs

ggourav mishra
16 min read

Setting Up Firebase Authentication in Your Next.js App? It's Easier Than You Think!

Hey folks! Ever wanted to add login and signup to your website but thought it was gonna be a huge pain? Well, guess what? It doesn't have to be! Especially if you're using Next.js and Firebase. Trust me, it's actually pretty straightforward.

In this guide, I'm gonna walk you through setting up Firebase Authentication in Next.js. We'll keep it super simple, no confusing jargon, just plain English. Think of it like I'm sitting next to you, showing you the ropes.

Why Bother with Firebase Authentication Anyway?

Okay, so why should you even care about Firebase Authentication? Good question! Basically, it handles all the complicated stuff around user logins for you. No need to build everything from scratch. That's a win, right?

Here's the deal:

  • It's Secure: Firebase is built by Google and they know their stuff when it comes to security. They handle things like password hashing and account security, so you don't have to worry as much. Security is a big deal online these days, so this is a huge plus.
  • Lots of Login Options: Firebase lets people sign in with email and password, Google, Facebook, Twitter, and a bunch of other ways. More choices for your users!
  • Easy to Use: Seriously, Firebase is designed to be developer-friendly. They give you all the tools you need to get authentication up and running quickly. And we're going to see just how easy it is in Next.js.
  • Scalable: If your app gets super popular (fingers crossed!), Firebase can handle tons of users without you having to do extra work. It grows with you.

So, yeah, Firebase Auth is pretty awesome for making user management easy in your web apps.

Let's Get Started: Firebase and Next.js Together

Ready to dive in? We're gonna break this down into easy steps.

Step 1: Create a Firebase Project (If You Don't Have One Yet)

First things first, you need a Firebase project. If you already have one, great, skip to the next part. If not, here's how:

  1. Go to the Firebase website.
  2. Click on "Go to console." (You might need to sign in with your Google account).
  3. Click "Add project."
  4. Give your project a name. Something like "NextjsAuthApp" works fine.
  5. Follow the steps to create your project. Google will guide you.

Once your project is created, you'll land on the Firebase project dashboard. We'll need some info from here soon, so keep this tab open.

Step 2: Set Up Authentication in Firebase

Now we need to enable authentication in your Firebase project. This is super quick:

  1. In your Firebase project console (the dashboard we just talked about), find "Authentication" in the left-hand menu and click on it.
  2. Click the "Get started" button.
  3. You'll see different "Sign-in methods." For this example, let's enable "Email/Password." Just hover over "Email/Password" and click the pencil icon (edit).
  4. Enable the switch to turn on "Email/Password" sign-in.
  5. Click "Save."

That's it for setting up authentication in Firebase itself! See? Not scary at all.

Step 3: Create a Next.js Project (or Use an Existing One)

Now let's get to the Next.js side of things. You'll need a Next.js project.

If you've already got a Next.js project, you can use that. If not, creating one is easy peasy:

Open your terminal (like Command Prompt on Windows or Terminal on Mac) and type:

npx create-next-app my-nextjs-auth-app
cd my-nextjs-auth-app
border-4 border-black bash

Replace "my-nextjs-auth-app" with whatever you want to name your project. This command sets up a brand new Next.js project for you.

Step 4: Install the Firebase JavaScript SDK

To use Firebase in our Next.js app, we need to install the Firebase JavaScript SDK (Software Development Kit). Think of it as the tools that let our Next.js app talk to Firebase.

In your terminal, inside your Next.js project folder, run this command:

npm install firebase
border-4 border-black bash

This will download and install the Firebase library into your project.

Step 5: Get Your Firebase Project Configuration

We need to tell our Next.js app how to connect to our Firebase project. For that, we need your Firebase project configuration. Remember that Firebase project dashboard we opened earlier? Let's go back there.

  1. In your Firebase project console, click the little settings icon (the gear icon) next to "Project Overview" in the top left corner.
  2. Choose "Project settings."
  3. Scroll down until you find the "Your apps" section. If you haven't added an app yet, click the web icon (</>). If you have, select your web app.
  4. Choose "Web" (the </>).
  5. Give your app a nickname (like "Next.js Web App") and click "Register app." You can skip setting up Firebase Hosting for now.
  6. On the next screen, you'll see a code snippet that looks like this:
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
border-4 border-black javascript

Important: Copy this firebaseConfig object! We'll need it in the next step.

Step 6: Initialize Firebase in Your Next.js App

Now, we're going to initialize Firebase in our Next.js app using the config we just copied.

  1. In your Next.js project, create a new file in the root directory (the main folder), and name it firebase.js (or firebaseConfig.js, whatever you like).
  2. Paste the firebaseConfig object you copied from Firebase into this file.
  3. Also, at the top of this file, add the Firebase initialization code. It should look something like this in your firebase.js file:
// firebase.js
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth'; // Import getAuth for authentication

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID"
};

const firebaseApp = initializeApp(firebaseConfig);
const auth = getAuth(firebaseApp); // Initialize Firebase Authentication

export { auth }; // Export auth for use in your components
border-4 border-black javascript

Make sure to replace the placeholder values in firebaseConfig with your actual Firebase project details.

We're also importing getAuth from firebase/auth and initializing Firebase Authentication with getAuth(firebaseApp). Then we export auth so we can use it in our Next.js components to handle logins and signups.

Step 7: Implement User Signup

Let's add a signup form to our Next.js app. We'll create a simple page for this.

  1. Go to your pages directory in your Next.js project.
  2. Create a new file named signup.js (or signup.jsx if you prefer).
  3. Inside signup.js, paste this code:
// pages/signup.js
import { useState } from 'react';
import { createUserWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../firebase'; // Import auth from firebase.js

export default function SignupPage() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const handleSignup = async (event) => {
    event.preventDefault();
    setError(''); // Clear any previous errors

    try {
      await createUserWithEmailAndPassword(auth, email, password);
      // Signup successful!  Maybe redirect to a logged-in page
      console.log('Signup successful!');
      // You can redirect the user here, for example to the home page
      // router.push('/'); // Uncomment if you have useRouter from next/router
    } catch (firebaseError) {
      setError(firebaseError.message);
      console.error('Signup error:', firebaseError);
    }
  };

  return (
    <div>
      <h1>Sign Up</h1>
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <form onSubmit={handleSignup}>
        <div>
          <label htmlFor="email">Email:</label>
          <input
            type="email"
            id="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
        <div>
          <label htmlFor="password">Password:</label>
          <input
            type="password"
            id="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
        <button type="submit">Sign Up</button>
      </form>
    </div>
  );
}
border-4 border-black javascript

Let's break down what's happening here:

  • We import useState from React to manage the email and password input fields.
  • We import createUserWithEmailAndPassword from firebase/auth. This is the Firebase function we use to create a new user account with email and password.
  • We import auth from our firebase.js file.
  • In the handleSignup function:
    • We prevent the default form submission behavior.
    • We clear any previous errors.
    • We use createUserWithEmailAndPassword(auth, email, password) to try and create a new user in Firebase Auth. We pass in our auth object, the email, and the password from the form.
    • If signup is successful, we log a success message to the console. You'll probably want to redirect the user to another page after signup, like a dashboard or homepage. (I've commented out a router example – you'd need to import useRouter from next/router if you want to use Next.js routing here.)
    • If there's an error during signup (like the email is already in use, or the password is too weak), we catch the error, set the error state, and log the error to the console.
  • The JSX (the stuff inside return()) creates a simple form with email and password fields and a "Sign Up" button. It also displays any error messages.

Now, if you run your Next.js app (npm run dev) and go to http://localhost:3000/signup, you should see a signup form. Try creating a new user! Then go back to your Firebase project console, go to "Authentication" -> "Users," and you should see the new user listed there. Cool, right?

Step 8: Implement User Login (Sign-in)

Signup is great, but people also need to log in! Let's add a login page.

  1. In your pages directory, create a new file named login.js (or login.jsx).
  2. Paste this code into login.js:
// pages/login.js
import { useState } from 'react';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../firebase'; // Import auth from firebase.js

export default function LoginPage() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const handleLogin = async (event) => {
    event.preventDefault();
    setError('');

    try {
      await signInWithEmailAndPassword(auth, email, password);
      // Login successful! Redirect to logged-in area
      console.log('Login successful!');
      // router.push('/'); // Redirect after login
    } catch (firebaseError) {
      setError(firebaseError.message);
      console.error('Login error:', firebaseError);
    }
  };

  return (
    <div>
      <h1>Log In</h1>
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <form onSubmit={handleLogin}>
        <div>
          <label htmlFor="email">Email:</label>
          <input
            type="email"
            id="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
        <div>
          <label htmlFor="password">Password:</label>
          <input
            type="password"
            id="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
        <button type="submit">Log In</button>
      </form>
    </div>
  );
}
border-4 border-black javascript

This login page code is very similar to the signup page. The main difference is:

  • We import signInWithEmailAndPassword from firebase/auth instead of createUserWithEmailAndPassword. This function is used to log in existing users.
  • The handleLogin function uses signInWithEmailAndPassword(auth, email, password) to try and sign the user in.

Now, if you go to http://localhost:3000/login, you should see a login form. Try logging in with the user you just created. If it works, you'll see "Login successful!" in the console (and again, you'd probably want to redirect the user to a logged-in page in a real app).

Step 9: Check User Authentication State (Are They Logged In?)

Okay, we can signup and login. But how do we know if a user is currently logged in when they visit our site? We need to check the authentication state. Firebase provides a way to do this easily.

Let's create a component that checks the user's auth state and displays a message.

  1. In your components directory (if you don't have one, create a folder named components in the root of your project), create a new file named AuthCheck.js (or AuthCheck.jsx).
  2. Paste this code into AuthCheck.js:
// components/AuthCheck.js
import { useEffect, useState } from 'react';
import { auth } from '../firebase'; // Import auth from firebase.js
import { onAuthStateChanged, signOut } from 'firebase/auth'; // Import auth state listener and signOut

export default function AuthCheck() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser); // Set user state
      setLoading(false); // Set loading to false once auth state is checked
    });

    return () => unsubscribe(); // Unsubscribe when component unmounts
  }, []);

  const handleSignOut = async () => {
    try {
      await signOut(auth);
      console.log('User signed out');
      // Optionally redirect after sign out
      // router.push('/');
    } catch (error) {
      console.error('Sign out error', error);
    }
  };

  if (loading) {
    return <p>Checking authentication...</p>; // Or a loading spinner
  }

  return (
    <div>
      {user ? (
        <div>
          <p>Welcome, {user.email}!</p>
          <button onClick={handleSignOut}>Sign Out</button>
        </div>
      ) : (
        <p>You are not logged in.</p>
      )}
    </div>
  );
}
border-4 border-black javascript

Here's what this AuthCheck component does:

  • We import useEffect and useState from React.
  • We import auth from firebase.js, and also onAuthStateChanged and signOut from firebase/auth.
    • onAuthStateChanged is a Firebase function that listens for changes in the user's authentication state. It tells us if the user logs in or logs out.
    • signOut lets us log the user out.
  • We use useState to:
    • user: to store the currently logged-in user (or null if no one is logged in).
    • loading: to track whether we are still checking the authentication state. We set it to true initially.
  • useEffect hook:
    • We use onAuthStateChanged(auth, (currentUser) => { ... }) to set up a listener. This listener runs whenever the authentication state changes.
    • Inside the listener:
      • setUser(currentUser): We update the user state with the currentUser object (Firebase provides this object, and it's null if no user is logged in).
      • setLoading(false): We set loading to false because we've now checked the auth state.
    • return () => unsubscribe(): This is important for cleanup! onAuthStateChanged sets up a listener that keeps running. When our component is removed from the screen, we need to stop listening to prevent memory leaks. The unsubscribe function returned by onAuthStateChanged does this.
  • handleSignOut function: Uses signOut(auth) to log the user out.
  • The JSX:
    • If loading is true, it shows "Checking authentication..."
    • After loading is false:
      • If user is not null (meaning a user is logged in), it shows "Welcome, [user's email]!" and a "Sign Out" button.
      • If user is null (no user logged in), it shows "You are not logged in."

Now, let's use this AuthCheck component on our homepage.

  1. Open pages/index.js.
  2. Import AuthCheck at the top:
import AuthCheck from '../components/AuthCheck';
border-4 border-black javascript
  1. Add the <AuthCheck /> component inside your HomePage function (or wherever you want to display the auth status):
// pages/index.js
import AuthCheck from '../components/AuthCheck';

export default function HomePage() {
  return (
    <div>
      <h1>Welcome to My App</h1>
      <AuthCheck />  {/* Add the AuthCheck component here */}
      {/* ... rest of your homepage content */}
    </div>
  );
}
border-4 border-black javascript

Now, if you go to http://localhost:3000/, you should see the AuthCheck component. If you're not logged in, it will say "You are not logged in." If you log in using the login page, and then go back to the homepage, it should now say "Welcome, [your email]!" and show a "Sign Out" button. Clicking "Sign Out" should log you out, and the homepage will update to say "You are not logged in."

Step 10: Protecting Pages (Making Some Pages Only Accessible to Logged-in Users)

One of the main reasons to have authentication is to protect certain parts of your website. Let's say you have a "dashboard" page that should only be visible to logged-in users. We can do this in Next.js using our AuthCheck component and some Next.js routing.

  1. Create a new page in pages called dashboard.js (or dashboard.jsx).
  2. In dashboard.js, you can use the AuthCheck component to conditionally render content based on whether the user is logged in. Here's an example:
// pages/dashboard.js
import AuthCheck from '../components/AuthCheck';
import { useRouter } from 'next/router'; // Import useRouter

export default function DashboardPage() {
  const router = useRouter(); // Initialize useRouter

  return (
    <div>
      <h1>Dashboard</h1>
      <AuthCheck>
        {/* This content will only be shown if the user is logged in */}
        <p>Welcome to your dashboard! You are logged in.</p>
        {/* ... Dashboard content goes here ... */}
      </AuthCheck>

      {/* If AuthCheck doesn't render its children (because user is not logged in),
          you might want to show a message or redirect to login page.
          AuthCheck component in this example *does* render children,
          but you could modify AuthCheck to *not* render children if not logged in,
          and handle the "not logged in" case here. */}
    </div>
  );
}
border-4 border-black javascript

In this dashboard.js page:

  • We import AuthCheck and useRouter from next/router.
  • We use <AuthCheck> as a wrapper around the dashboard content. Anything inside <AuthCheck> will only be rendered if a user is logged in.
  • If you want to redirect users to the login page if they try to access the dashboard when they're not logged in, you could modify your AuthCheck component to handle redirects, or you could add a redirect in the DashboardPage itself if AuthCheck indicates the user is not logged in.

A more robust way to protect pages is to use Server-Side Rendering (SSR) or Middleware in Next.js to check authentication before the page is even rendered. For simplicity, this example shows client-side checking with AuthCheck, but for sensitive pages, server-side checks are generally recommended.

A Few Extra Tips and Things to Think About

  • Error Handling: We added basic error messages to the signup and login forms, but you'll want to make your error handling more user-friendly in a real app. Firebase Auth errors often give you specific error codes that you can use to show more helpful messages to the user (e.g., "Incorrect password," "Email address is already in use," etc.). Check the Firebase documentation for error codes.
  • Password Reset: Firebase Authentication also provides features for password reset. You might want to add a "Forgot Password?" link on your login page and implement password reset functionality using Firebase's tools.
  • Email Verification: For security, you might want to enable email verification in Firebase Authentication. This makes sure that users verify their email address after signing up.
  • Social Sign-in: Firebase supports sign-in with Google, Facebook, Twitter, etc. Setting these up involves a little more configuration in the Firebase console and in your Next.js app, but Firebase provides documentation for each provider.
  • User Data: Firebase Authentication handles user login, but it doesn't automatically store extra user data (like name, profile picture, etc.). If you need to store additional user info, you can use Firebase Firestore (a database) or another database solution to store and manage user profiles linked to their Firebase Auth user IDs.

Wrapping Up

And there you have it! You've taken your first steps in setting up Firebase Authentication in your Next.js application. We covered signup, login, checking auth state, and even protecting pages. It's a pretty powerful system, and we've just scratched the surface.

Firebase Authentication makes handling user logins much easier than building everything yourself. Hopefully, this guide has made it feel less intimidating and more like something you can actually implement in your own projects.

Now go build something awesome and secure! Happy coding!

javascript
web dev
next js
Firebase
authentication
Next.js
souravvmishra
tech
education
personal
Manyblogs
Manyblogs