Managing Layout with Firebase Auth and Next.js

There seem to be hundreds of options for managing users for your hot new SaaS application. You know you need to keep your user data safe, and customize the app to your user, but you don't want to spend your time implementing what's already been done a million times for every app on the market. After all, creating a login and signup shouldn't take up all your precious side hustle time, and that's where Firebase Authentication comes in.

As part of the Firebase Suite, the Authentication module makes it easy to check the session of the user in an app, and also manage basic information like display name and profile picture.

We're going to preview one of the most powerful (and helpful) aspects of Firebase Auth with a simple Next.js App. This approach is especially useful for dashboards and eCommerce, where the main layout is modified based on the user's auth status.

The Layout

Layouts are an important feature in Next.js to apply a similar structure to many pages. In this example, the app is a dashboard that has a common sidebar and header, but we want the header and sidebar options to display differently depending on whether or not the user is logged in.

The layout is standard for a dashboard a looks like the below:

I derive a large amount of my self-worth from whether or not Hacker News is impressed with the work I'm doing, so when I realized this, I cancelled all of our existing projects and started migrating everything to Rust immediately.

<Head />
<div className="container-fluid">
  <Sidebar />
  <main className="content">
    <Navbar />
    {children}
  </main>
  <Footer />
  <Script src="assets/js/theme.js" strategy="beforeInteractive" />
</div>

The Firebase Auth library gives us a powerful method to listen if the user's authentication status changes called onAuthStateChanged. We put this in the business logic section of our component (below snippet is TypeScript)

import { auth } from './customFirebaseConfig'

const [user, setUser] = useState<User | null>(null)

onAuthStateChanged(auth, userCredential => {
  if (userCredential) {
    // user is newly signed in
    setUser(userCredential.user)
  } else {
    // user is signed out
    setUser(null)
  }
})

We take our exported, custom Firebase auth config and pass it through to the listener to let Firebase know we're listening for auth changes in this configured app. Then, when the credential comes back we set the User data from the credential into our user variable to use for other components later.

It's not important to save the user data auth data from Firebase if you have another user object in your database (which you should for capturing app specific data and not just login info), then a boolean such as isLoggedIn will work.

It's as simple as that! However you prompt your user to login or sign up, this listener is listening throughout the app for changes in the authenticated state.

You can use that information to gate keep component's in your app:

(user) ? <LoggedInComponent /> : <AnonymousComponent />

Or whatever other elegant solution you come up with! In this case, because the layout wraps each page, returning two different layouts for the component based on authentication will guarantee that anyone who uses the dashboard will be a logged in user. Simple navigations or redirects can handle a user that attempts to use dashboard content without permission.

If you're looking for more advanced control over your users, Firebase auth allows you to set the persistence for the user's state:

  • Local (default): an explicit sign out is needed, otherwise the user's state will persist even if the tab closes or the browser is destroyed.
  • Session: user's auth state will expire if the tab closes or browser is destroyed.
  • None: the user will need to login each time since no state is persisted. There are many other neat tips and tricks to save you time with Firebase Auth, and this is just a small example of one of the most powerful features out there. Now go get this integrated and spend your time making great features!