Back to articles

Building a Fullstack SaaS with React, Supabase, and Stripe

AuthorMajd Muhtaseb09/15/202512 minutes
Building a Fullstack SaaS with React, Supabase, and Stripe

Introduction

Building a SaaS application from scratch can be daunting. This article simplifies the process by guiding you through building a basic SaaS with React, Supabase, and Stripe. React handles the user interface, Supabase provides a scalable backend, and Stripe manages payments.

Setting up Supabase

First, create a new project on Supabase. You'll need your Supabase URL and anon key. Store these in your .env file:

SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY

Create a users table with at least an id (UUID, primary key) and email column. Enable Row Level Security (RLS) and configure policies to allow users to only access their own data.

React Frontend with Authentication

Use create-react-app or your preferred React setup. Install @supabase/supabase-js:

npm install @supabase/supabase-js

Create a supabaseClient.js:

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

const supabaseUrl = process.env.REACT_APP_SUPABASE_URL;
const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

Implement authentication using Supabase's auth methods. Here's a basic sign-up component:

import { useState } from 'react';
import { supabase } from './supabaseClient';

function SignUp() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    const { data, error } = await supabase.auth.signUp({
      email: email,
      password: password,
    });

    if (error) {
      console.error('Error signing up:', error);
    } else {
      console.log('Sign up successful:', data);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />
      <input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} />
      <button type="submit">Sign Up</button>
    </form>
  );
}

export default SignUp;

Integrating Stripe for Payments

Install stripe and @stripe/react-stripe-js:

npm install @stripe/stripe-js @stripe/react-stripe-js

Create a Stripe account and obtain your Publishable Key and Secret Key. Define your pricing plans within Stripe.

On your server (e.g., a Supabase Edge Function or a Node.js API), create an endpoint that uses the Stripe Secret Key to create a Stripe Checkout Session. This session will redirect the user to Stripe to enter their payment information.

Example (using Node.js and Express):

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const express = require('express');
const app = express();

app.post('/create-checkout-session', async (req, res) => {
  const session = await stripe.checkout.sessions.create({
    line_items: [
      {
        price: 'YOUR_STRIPE_PRICE_ID', // Replace with your actual Price ID
        quantity: 1,
      },
    ],
    mode: 'subscription',
    success_url: `${YOUR_DOMAIN}/success`,
    cancel_url: `${YOUR_DOMAIN}/cancel`,
  });

  res.redirect(303, session.url);
});

app.listen(4242, () => console.log('Running on port 4242'));

In your React app, use the @stripe/react-stripe-js to redirect the user to Stripe. You'll need to load Stripe.js with your publishable key:

import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';

const stripePromise = loadStripe('YOUR_STRIPE_PUBLISHABLE_KEY');

function App() {
  return (
    <Elements stripe={stripePromise}>
      {/* Your components that use Stripe */}
    </Elements>
  );
}

Create a button that calls the /create-checkout-session endpoint.

Handling Webhooks

Configure Stripe webhooks to listen for events like checkout.session.completed and customer.subscription.created. Use these webhooks to update your Supabase database to reflect the user's subscription status. This ensures that you can control access to features based on subscription level. Securely verify the webhook signatures to prevent tampering.

Conclusion

This guide provides a basic framework for building a SaaS application. You can expand upon this foundation by adding more features, implementing more robust error handling, and optimizing performance. Remember to prioritize security best practices throughout the development process.