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.
Prerequisites
- 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
Set up the subscription data structure
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.
Build content gating with conditionals
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.
Handle expired subscriptions
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.
Build the upgrade flow
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.
Implement downgrade and cancellation
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 working example
1SUBSCRIPTION FEATURES — WORKFLOW SUMMARY2==========================================34OPTION SET: Plan5 Free: $0, basic features6 Pro: $29, premium features7 Business: $99, all features + priority support89USER FIELDS:10 current_plan (Plan Option Set)11 subscription_status (text: active/cancelled/expired)12 stripe_subscription_id (text)13 subscription_end_date (date)1415CONTENT GATING:16 Group Premium Content:17 Visible when: Current User's current_plan is not Free18 AND subscription_status is active19 Group Upgrade Prompt:20 Visible when: Current User's current_plan is Free21 OR subscription_status is not active2223WORKFLOW 1: Upgrade24 Event: Button Select Plan is clicked25 Action 1: Stripe Checkout (plan price * 100)26 On success:27 Action 2: Make changes to Current User28 current_plan = selected Plan29 subscription_status = active30 subscription_end_date = Current date + 1 month3132WORKFLOW 2: Cancel33 Event: Button Cancel is clicked34 Action 1: Call Stripe API - Cancel Subscription35 Action 2: Make changes to Current User36 subscription_status = cancelled3738BACKEND: check_expired_subscriptions (daily)39 Search: Users where end_date < now AND status = active40 Action: Make changes to list41 subscription_status = expired42 current_plan = FreeCommon mistakes when building subscriptions in Bubble
Why it's a problem: Relying only on element visibility for content gating
How to avoid: Use Privacy Rules to prevent premium content data from being sent to free users at all.
Why it's a problem: Not checking subscription expiry
How to avoid: Run a daily backend workflow that checks subscription_end_date and updates expired accounts.
Why it's a problem: Applying plan changes immediately instead of at period end
How to avoid: 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
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a Bubble.io app with subscription tiers (Free, Pro, Business). I need to gate content based on the user's plan, handle expired subscriptions, and build upgrade/downgrade flows with Stripe. How do I set this up?
Add subscription-based content gating. Check the user's plan before showing premium content. Show upgrade prompts for free users. Build upgrade and cancellation workflows with Stripe and handle expired subscriptions automatically.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation