Building a Full-Stack E-Commerce Site with Lovable: From Zero to Production



This content originally appeared on DEV Community and was authored by Cathy Lai

Introduction

In this tutorial, I’ll walk you through how I built a complete e-commerce vegetable store from scratch using Lovable. What started as a simple idea turned into a fully functional online store with shopping cart, payment processing, user authentication, and order management—all built in record time.

By the end of this guide, you’ll understand how to leverage modern web technologies to create your own e-commerce platform without writing backend code from scratch.

What We Built

Our vegetable e-commerce store includes:

  • 🛒 Shopping Cart System with add/remove functionality
  • 💳 Stripe Payment Integration for secure checkout
  • 👤 User Authentication (sign up/login)
  • 📦 Order Management with persistent order history
  • ☁ Lovable Cloud Backend for database and authentication
  • 🔗 GitHub Integration for version control

Tech Stack

  • Frontend: React 18 + TypeScript + Vite
  • Styling: Tailwind CSS with custom design system
  • UI Components: Radix UI + shadcn/ui
  • State Management: React Context API
  • Backend: Lovable Cloud (Supabase-powered)
  • Payments: Stripe
  • Routing: React Router v6

Part 1: Setting Up the Foundation

The Product Catalog

First, I created a simple product data structure in src/data/products.ts:

export const products = [
  {
    id: 1,
    name: "Fresh Tomatoes",
    price: 4.99,
    image: "/src/assets/tomatoes.jpg",
    description: "Ripe and juicy organic tomatoes"
  },
  // ... more products
];

This approach keeps our product data centralized and easy to manage. For a production app, you’d fetch this from a database, but starting with static data helps you prototype quickly.

Building the Product Display

I created a reusable ProductCard component that displays each product with an image, price, and “Add to Cart” button. Key features:

  • Responsive design using Tailwind CSS
  • Hover effects for better UX
  • Integration with the shopping cart context
<Card className="overflow-hidden hover:shadow-lg transition-shadow">
  <img src={product.image} alt={product.name} />
  <CardContent>
    <h3>{product.name}</h3>
    <p>${product.price}</p>
    <Button onClick={() => addToCart(product)}>Add to Cart</Button>
  </CardContent>
</Card>

Part 2: Implementing the Shopping Cart

Cart Context Setup

The shopping cart is managed through React Context (src/contexts/CartContext.tsx), making cart state accessible throughout the app:

const CartContext = createContext<CartContextType | undefined>(undefined);

export const CartProvider = ({ children }: { children: ReactNode }) => {
  const [cartItems, setCartItems] = useState<CartItem[]>([]);

  const addToCart = (product: Product) => {
    // Logic to add or increment quantity
  };

  const removeFromCart = (productId: number) => {
    // Logic to remove or decrement quantity
  };

  return (
    <CartContext.Provider value={{ cartItems, addToCart, removeFromCart, ... }}>
      {children}
    </CartContext.Provider>
  );
};

Cart Features

The cart includes:

  • Quantity Management: Increment/decrement items
  • Automatic Calculations: Total price updated in real-time
  • Persistent State: Cart survives page refreshes (localStorage)
  • Visual Feedback: Toast notifications when items are added

Cart Page

The dedicated cart page (src/pages/Cart.tsx) shows:

  • All items in the cart with images and prices
  • Quantity controls for each item
  • Running total calculation
  • Checkout button that leads to payment

Part 3: Stripe Payment Integration

Setting Up Stripe

Integrating Stripe for payments involved several components:

  1. Backend Edge Function (supabase/functions/create-checkout/index.ts):
    • Creates Stripe checkout sessions
    • Handles payment intent creation
    • Associates orders with authenticated users
const session = await stripe.checkout.sessions.create({
  payment_method_types: ['card'],
  line_items: items.map(item => ({
    price_data: {
      currency: 'usd',
      product_data: { name: item.name },
      unit_amount: Math.round(item.price * 100),
    },
    quantity: item.quantity,
  })),
  mode: 'payment',
  success_url: `${origin}/orders`,
  cancel_url: `${origin}/cart`,
});
  1. Frontend Checkout Page (src/pages/Checkout.tsx):

    • Integrates Stripe Elements
    • Handles payment form submission
    • Provides loading states and error handling
  2. Payment Confirmation (supabase/functions/confirm-payment/index.ts):

    • Verifies payment status
    • Creates order records in the database
    • Returns order confirmation to the user

The Checkout Flow

  1. User clicks “Proceed to Checkout” from cart
  2. Cart items are sent to the backend
  3. Stripe checkout session is created
  4. User enters payment details
  5. Payment is processed securely by Stripe
  6. Order is confirmed and saved to database
  7. User is redirected to orders page

Part 4: Lovable Cloud Backend

Why Lovable Cloud?

Instead of manually setting up a backend server, database, and authentication system, Lovable Cloud provides everything out of the box. It’s powered by Supabase but fully integrated into the Lovable platform.

Database Setup

I created an orders table using a database migration:

CREATE TABLE public.orders (
  id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id UUID NOT NULL,
  items JSONB NOT NULL,
  total DECIMAL(10,2) NOT NULL,
  status TEXT DEFAULT 'pending',
  created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);

-- Enable Row Level Security
ALTER TABLE public.orders ENABLE ROW LEVEL SECURITY;

-- Users can only see their own orders
CREATE POLICY "Users can view own orders" 
ON public.orders 
FOR SELECT 
USING (auth.uid() = user_id);

Row Level Security (RLS)

RLS policies ensure users can only access their own orders—critical for security and privacy. The policy checks that the authenticated user’s ID matches the user_id in the order record.

Part 5: User Authentication

Authentication Context

User authentication is managed through AuthContext (src/contexts/AuthContext.tsx):

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    // Check current session
    supabase.auth.getSession().then(({ data: { session } }) => {
      setUser(session?.user ?? null);
    });

    // Listen for auth changes
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (_event, session) => {
        setUser(session?.user ?? null);
      }
    );

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

  return (
    <AuthContext.Provider value={{ user, signIn, signUp, signOut }}>
      {children}
    </AuthContext.Provider>
  );
};

Sign Up & Login Pages

I created dedicated pages for authentication:

Sign Up (src/pages/Signup.tsx):

  • Email and password registration
  • Form validation
  • Automatic redirect after successful signup

Login (src/pages/Login.tsx):

  • Email and password authentication
  • “Remember me” functionality
  • Password reset option

Auto-Confirm Email Setup

For development and testing, I enabled auto-confirm for email signups so users don’t need to verify their email before accessing the app. This can be adjusted for production environments.

Protected Routes

The navigation automatically shows/hides features based on authentication status:

{user ? (
  <>
    <Link to="/orders">My Orders</Link>
    <Button onClick={signOut}>Logout</Button>
  </>
) : (
  <>
    <Link to="/login">Login</Link>
    <Link to="/signup">Sign Up</Link>
  </>
)}

Part 6: Order Management

Orders Page

The Orders page (src/pages/Orders.tsx) fetches and displays a user’s order history:

useEffect(() => {
  const fetchOrders = async () => {
    const { data, error } = await supabase
      .from('orders')
      .select('*')
      .order('created_at', { ascending: false });

    if (data) setOrders(data);
  };

  if (user) fetchOrders();
}, [user]);

Each order shows:

  • Order ID and date
  • Items purchased with quantities
  • Total amount paid
  • Order status (pending, completed, etc.)

Order Creation Flow

When a payment is confirmed:

  1. Backend function verifies Stripe payment
  2. Order record is created in database
  3. User ID is attached to the order
  4. User can view order in their order history

Part 7: Design System

Tailwind Configuration

I set up a comprehensive design system using Tailwind CSS with custom semantic tokens defined in src/index.css:

:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 142 76% 36%;
  --primary-foreground: 355.7 100% 97.3%;
  /* ... more tokens */
}

Benefits of Semantic Tokens

Instead of using colors directly (like bg-white or text-black), semantic tokens provide:

  • Consistent theming across the entire app
  • Easy dark mode implementation
  • Centralized design control
  • Better maintainability

Component Styling

All components use these semantic tokens:

<Button className="bg-primary text-primary-foreground hover:bg-primary/90">
  Add to Cart
</Button>

Part 8: Connecting to GitHub

Why GitHub Integration?

Connecting your Lovable project to GitHub provides:

  • Version Control: Track all changes to your codebase
  • Collaboration: Work with team members
  • Backup: Automatic code backup
  • Deployment Options: Deploy to various hosting platforms
  • Professional Workflow: Industry-standard development practices

How to Connect

Connecting to GitHub is straightforward:

  1. Click the GitHub Button: Located in the top-right of the Lovable editor
  2. Authorize Lovable: Grant permissions to the Lovable GitHub App
  3. Create Repository: Choose a repository name and visibility (public/private)
  4. Automatic Sync: Code syncs bidirectionally between Lovable and GitHub

Bidirectional Sync

Once connected:

  • Changes in Lovable automatically push to GitHub
  • Changes in GitHub can be pulled into Lovable
  • Commit history is preserved
  • Branch management is supported

Development Workflow

With GitHub connected, you can:

  • Clone locally: Work in your favorite IDE (VS Code, WebStorm, etc.)
  • Create branches: Experiment with features safely
  • Review changes: Use GitHub’s diff viewer
  • Collaborate: Invite team members to contribute
  • Deploy easily: Connect to Vercel, Netlify, or other platforms

Key Takeaways

What I Learned

  1. Start Simple: Begin with static data and basic features, then iterate
  2. Context is Powerful: React Context handles state management elegantly for small-to-medium apps
  3. Backend-as-a-Service: Lovable Cloud eliminates backend setup complexity
  4. Security First: RLS policies protect user data automatically
  5. Design Systems Matter: Semantic tokens make styling consistent and maintainable

Performance Considerations

  • Lazy Loading: Images load on-demand for better performance
  • React Query: Could be added for better data fetching and caching
  • Code Splitting: Vite handles this automatically with dynamic imports

Conclusion

Building a full-stack e-commerce site doesn’t have to be overwhelming. With modern tools like Lovable, you can focus on creating value for your users rather than wrestling with infrastructure.

The combination of React for the frontend, Lovable Cloud for the backend, and Stripe for payments provides a solid foundation that can scale from prototype to production.

Whether you’re building your first e-commerce site or your tenth, the patterns and practices in this tutorial will help you ship faster and more confidently.

Resources

Built with ❤ using Lovable


This content originally appeared on DEV Community and was authored by Cathy Lai