⚠️ This post has been published a while ago. Some of the information might be outdated.
Supabase is an open source managed back-end platform. It’s a direct alternative to Firebase, which is owned by Google and closed source.
Supabase comes with features such as authentication, object storage, and managed databases. Everything is built on top of open source tools, such as PostgREST and GoTrue. If you want, you can also self-host your own instance of Supabase. As of today, Supabase is in public Beta.
In this tutorial you will learn how to build an a simple React application with authentication using Create React App (CRA). Supabase will serve as the back-end for the authentication part. The application will include sign in, sign up, and a private route than can only be accessed with valid credentials.
If you wanna jump straight to the code, you can check the GitHub repository.
Setting up Supabase
Visit Supabase’s website to create a new account. Click the “Start your project” button and sign in with your GitHub account.
After signing in to the dashboard, hit the green “New Project” button. A modal like this should appear:
1. Create a new project on Supabase
Choose a name for your project and a region close to you. It’s required that you set a database password too, but we won’t be using any on this tutorial.
It will take a few minutes for the project to be fully created. After it’s done, go to Settings > API and copy the URL & Public Anonymous API key. Save the values somewhere, you will need them later on.
2. Copy public credentials
đź’ˇ Tip: To make it easier to test the sign up flow, you can also disable email confirmations on Authentication > Settings > Disable Email Confirmations.
Setting up the project
Create a new project using Create React App:
3. Create a new CRA project
I usually do some cleanup on new CRA projects before start developing. Here’s how the project structure looks like after moving files around and deleting a few imports:
4. Overview of the folder structure after cleaning up CRA files
Feel free to recreate the same file structure. Don’t worry about adding any code or trying to make sense of all the components just yet, we will go through everything later.
The src/index.js and src/components/App.js were already created by CRA. Here’s how they look after cleaning up:
First, install the Supabase JavaScript client library on your project:
7. Install the Supabase client
Now add the code to initialize Supabase on src/supabase.js:
8. Supabase client initalization
In your .env.local file, add the URL and Public Anonymous API Key saved from the first step:
9. Set Supabase configuration variables
Create authentication pages
Let’s write the code for the Signup, Login and Dashboard components. These will be the three main pages of the application.
For now, let’s just focus on writing a boilerplate for those components, without any authentication logic. Start by writing the Signup component:
10. Initial code for the Signup component
The Login component looks very similar to Signup, with a few differences:
11. Initial code for the Login component
The Dashboard is a simple component that displays a greeting message and offers the user to sign out:
12. Initial code for the Dashboard component
Routing components with React Router
So far the components are isolated. There is no routing between the Signup, Login and Dashboard pages.
Let’s work on that by adding React Router to the project:
13. Install React Router
In src/components/App.js, declare a route for each of the components created before:
14. Declare routes in the App component
Let’s also add links to navigate between the Signup and Login components:
15. Add link between Signup and Login pages16. Add link between Signup and Login pages
You can test the navigation between components by running the project and clicking on the links or changing the URL in the navigation bar:
17. Demo of the navigation working
Shameless plug: you may notice that the HTML looks a bit different, that’s because I am using axist, a tiny drop-in CSS library that I built.
Adding the authentication logic
To set up the authentication logic for the app, we’re going to use React’s Context API.
The Context API allows sharing data to a tree of components without explicitly passing props through every level of the tree. It’s used to share data that is considered “global” (within that component tree).
In this tutorial, we will use Context to share data associated with the user and the authentication operations. All this information will come from Supabase and will be needed on multiple parts of the app.
Let’s start by adding code on src/contexts/Auth.js. First, create a Context object:
18. Create the Context object
Now, in the same file, create a Provider component called AuthProvider:
19. Create the AuthProvider
The AuthProvider does three things:
Calls supabase.auth.session to find out the current state of the user and update the user object.
Listens for changes in the authentication state (user signed in, logged out, created a new account, etc.) by subscribing to supabase.auth.onAuthStateChange function.
Prepares the object that will be shared by its children components (value prop). In this case, any components down the tree will have access to the signUp, signIn, signOut functions and the user object. They will be used by the Signup, Login and Dashboard components later on.
The loading state property will make sure the child components are not rendered before we know anything about the current authentication state of the user.
Now, create a useAuth function to help with accessing the context inside the children components:
20. Create the useAuth function
You can check how the src/contexts/Auth.js looks after all the changes on the GitHub repository.
Lastly, we need to wrap the Signup, Login and Dashboard components with the AuthProvider:
21. Add the AuthProvider to App.js
Adding authentication to the components
Remember the @TODOs you left earlier in the components? Now it’s time to, well, do them.
The functions needed by the components - signUp, signIn and signOut - as well as the user object are available through the Context. We can now get those values using the useAuth function.
Let’s start by adding the sign up logic to the Signup component:
22. Add authentication logic to the Signup component
The Login component will look very similar. The main difference is that you will call signIn instead of signUp:
23. Add authentication logic to the Login component
Lastly, change the Dashboard so the user can sign out of the application. You can also display some basic information together with the greeting message, such as the user ID:
24. Add authentication logic to the Dashboard component
Protecting routes
Currently, all the authentication logic is in place, but the Dashboard component remains publicly accessible. Anyone who happens to fall on locahost:3000 would see a broken version of the dashboard.
Let’s fix that by protecting the route. If a user that is not authenticated tries to access it, they will be redirected to the login page.
Start by creating a PrivateRoute component:
25. Create the PrivateRoute component
The PrivateRoute wraps the Route component from React Router and passes down the props to it. It only renders the page if the user object is not null (the user is authenticated).
If the userobject is empty, a redirect to the login page will be made by Redirect component from React Router.
Finally, update the dashboard route in the App component to use a PrivateRoute instead:
26. Update the Dashboard component to use PrivateRoute
Done! The dashboard is only available for authenticated users.
Final result
This is how the final version of the application should look like:
26. Final demo of the application
You can see the sign up, login, and sign out working. The dashboard page is also protected, attempting to access it by changing the URL redirects the user to the login page. Notice the user ID showing there too.
Going further
There are a few things that we could add for a more complete authentication flow:
Password reset. I intentionally left it out for simplicity, but Supabase supports password reset through their API. It does all the heavy-lifting for you, including sending the email to the user with the reset instructions.
Authentication providers. When logging in, you can also specify different authentication providers like Google, Facebook and GitHub. Check out the docs.
User operations. You can also add metatada to the authenticated user using the update function. With this, you could build for example a basic user profile page.