# How to build subscriptions in Bubble

- Tool: Bubble
- Difficulty: Beginner
- Time required: 20-25 min
- Compatibility: All Bubble plans
- Last updated: March 2026

## TL;DR

Gate content by subscription tier in Bubble by checking the user's subscription status before showing premium content, handling expired subscriptions with upgrade prompts, and building upgrade and downgrade flows. This ensures only paying subscribers access your premium features.

## Overview: Adding User Subscription Features in Bubble

This tutorial shows you how to gate app content by subscription tier. You will check the user's active subscription before showing premium content, display upgrade prompts for free users, handle expired subscriptions gracefully, and build the upgrade/downgrade flow.

## Before you start

- A Bubble account with an existing app
- Stripe plugin configured for subscription payments
- A Subscription or Plan data type already created
- Basic understanding of Bubble conditionals and workflows

## Step-by-step guide

### 1. Set up the subscription data structure

Go to the Data tab and ensure you have: a Plan Option Set with tiers (Free, Pro, Business) and attributes (monthly_price, features list). On the User data type, add fields: current_plan (Option Set: Plan), subscription_status (text: active, cancelled, expired), stripe_subscription_id (text), and subscription_end_date (date).

**Expected result:** User has subscription-related fields and a Plan Option Set defines available tiers.

### 2. Build content gating with conditionals

On pages with premium content, wrap gated sections in a Group. Add a conditional: When Current User's current_plan is Free or Current User's subscription_status is not active — hide this Group. Show a different Group with an upgrade prompt: This feature is available on Pro and above. Upgrade now. For individual elements, use conditionals to show/hide based on the plan tier. Always use Privacy Rules as the security layer — conditionals only control UI visibility.

> Pro tip: Put the security check in Privacy Rules, not just element visibility. Hidden elements can still be accessed by tech-savvy users.

**Expected result:** Premium content is hidden from free users with upgrade prompts shown instead.

### 3. Handle expired subscriptions

Create a backend workflow called check_expired_subscriptions that runs daily. Search for Users where subscription_end_date < Current date/time and subscription_status = active. For each, change subscription_status to expired and current_plan to Free. On page load, check if the user's subscription has expired: When Current User's subscription_status is expired, show a banner: Your subscription has expired. Renew to continue accessing premium features.

**Expected result:** Expired subscriptions are automatically detected and users see renewal prompts.

### 4. Build the upgrade flow

Create an upgrade page showing the Plan Option Set in a Repeating Group with pricing, features, and a Select Plan button per tier. The workflow: trigger Stripe Checkout for the selected plan's price. On payment success, update the User: current_plan = selected plan, subscription_status = active, stripe_subscription_id = Stripe result, subscription_end_date = one month from now.

**Expected result:** Users can select a plan, pay via Stripe, and immediately access premium content.

### 5. Implement downgrade and cancellation

On the account settings page, show the current plan with a Change Plan button. For downgrades, use Stripe's API to update the subscription to the lower plan — the change takes effect at the end of the current billing period. For cancellation, call Stripe's cancel subscription endpoint. Update the User: subscription_status = cancelled. The user keeps access until subscription_end_date. For complex subscription management with usage-based billing, consider working with RapidDev.

**Expected result:** Users can downgrade or cancel their subscription, with access continuing until the end of the billing period.

## Complete code example

File: `Workflow summary`

```text
SUBSCRIPTION FEATURES — WORKFLOW SUMMARY
==========================================

OPTION SET: Plan
  Free:     $0,  basic features
  Pro:      $29, premium features
  Business: $99, all features + priority support

USER FIELDS:
  current_plan (Plan Option Set)
  subscription_status (text: active/cancelled/expired)
  stripe_subscription_id (text)
  subscription_end_date (date)

CONTENT GATING:
  Group Premium Content:
    Visible when: Current User's current_plan is not Free
      AND subscription_status is active
  Group Upgrade Prompt:
    Visible when: Current User's current_plan is Free
      OR subscription_status is not active

WORKFLOW 1: Upgrade
  Event: Button Select Plan is clicked
  Action 1: Stripe Checkout (plan price * 100)
  On success:
  Action 2: Make changes to Current User
    current_plan = selected Plan
    subscription_status = active
    subscription_end_date = Current date + 1 month

WORKFLOW 2: Cancel
  Event: Button Cancel is clicked
  Action 1: Call Stripe API - Cancel Subscription
  Action 2: Make changes to Current User
    subscription_status = cancelled

BACKEND: check_expired_subscriptions (daily)
  Search: Users where end_date < now AND status = active
  Action: Make changes to list
    subscription_status = expired
    current_plan = Free
```

## Common mistakes

- **Relying only on element visibility for content gating** — Hidden elements are still in the page HTML and can be accessed via browser developer tools Fix: Use Privacy Rules to prevent premium content data from being sent to free users at all.
- **Not checking subscription expiry** — Users with expired subscriptions continue accessing premium content indefinitely Fix: Run a daily backend workflow that checks subscription_end_date and updates expired accounts.
- **Applying plan changes immediately instead of at period end** — Downgrading immediately after payment means the user paid for a higher tier they cannot use Fix: Schedule downgrades to take effect at the subscription_end_date using Stripe's built-in proration handling.

## Best practices

- Use Privacy Rules as the security layer for content gating, not just visibility conditionals
- Check subscription expiry daily with a backend workflow
- Show clear upgrade prompts instead of just hiding content
- Handle downgrades at the end of the billing period
- Display current plan and renewal date on the account settings page
- Store the stripe_subscription_id for managing subscriptions via API
- Offer a grace period before revoking access on expired subscriptions

## Frequently asked questions

### How do I handle Stripe webhooks for subscription changes?

Set up a Stripe webhook endpoint using Bubble's API workflows. Listen for events like invoice.paid, customer.subscription.updated, and customer.subscription.deleted to keep your User records in sync.

### Can I offer annual billing discounts?

Yes. Create separate Stripe Prices for monthly and annual billing. Display both options on the pricing page with the annual discount highlighted.

### What happens if a payment fails?

Stripe automatically retries failed payments. Listen for the invoice.payment_failed webhook event and update the user's subscription_status to past_due with a warning banner.

### Can RapidDev help with subscription management?

Yes. RapidDev specializes in Bubble development and can build complete subscription systems with Stripe webhooks, usage-based billing, team subscriptions, and customer portal integration.

### Should I use Stripe's Customer Portal?

Yes. Stripe's Customer Portal lets users manage their subscription, update payment methods, and view invoices without you building that UI. Link to it from your account settings page.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/add-a-user-subscription-feature-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/add-a-user-subscription-feature-in-bubble
