flowglad-feature-gating
Implement feature access checks using Flowglad to gate premium features, create paywalls, and restrict functionality based on subscription status. Use this skill when adding paid-only features or checking user entitlements.
Install
mkdir -p .claude/skills/flowglad-feature-gating && curl -L -o skill.zip "https://mcp.directory/api/skills/download/7257" && unzip -o skill.zip -d .claude/skills/flowglad-feature-gating && rm skill.zipInstalls to .claude/skills/flowglad-feature-gating
About this skill
Feature Gating
Abstract
Implement feature access checks using Flowglad's checkFeatureAccess method to gate premium features, create paywalls, and restrict functionality based on subscription status.
Table of Contents
- Loading State Handling — CRITICAL
- Server-Side Gating — HIGH
- Feature Identification — MEDIUM
- Component Wrapper Patterns — MEDIUM
- Redirect to Upgrade Patterns — MEDIUM
1. Loading State Handling
Impact: CRITICAL
The billing hook loads asynchronously. While loading, checkFeatureAccess is null (not a function). If you try to call it before loading completes, you'll get a runtime error or incorrect behavior. This causes premium users to see upgrade prompts or paywalls incorrectly.
Note: The
flowglad()factory function used in server-side examples must be set up in your project (typically at@/lib/flowglad). See the setup skill for configuration instructions.
1.1 Wait for Billing to Load
Impact: CRITICAL (prevents flash of incorrect content)
Users with active subscriptions will see upgrade prompts flash briefly if you don't wait for billing to load before checking access.
Incorrect: checks access before billing loads
function PremiumFeature() {
const { checkFeatureAccess } = useBilling()
// BUG: checkFeatureAccess is null while loading!
// This will throw: "checkFeatureAccess is not a function"
if (!checkFeatureAccess('premium-feature')) {
return <UpgradePrompt />
}
return <PremiumContent />
}
This crashes because checkFeatureAccess is null until billing data loads, not a callable function.
Correct: check both loaded and checkFeatureAccess
function PremiumFeature() {
const { loaded, checkFeatureAccess } = useBilling()
if (!loaded || !checkFeatureAccess) {
return <LoadingSkeleton />
}
if (!checkFeatureAccess('premium-feature')) {
return <UpgradePrompt />
}
return <PremiumContent />
}
Always check both loaded and checkFeatureAccess before calling the function to ensure billing data is available.
1.2 Skeleton Loading Patterns
Impact: CRITICAL (prevents layout shift)
Show appropriate loading states that match the expected content dimensions to prevent layout shift.
Incorrect: shows nothing or spinner
function Dashboard() {
const { loaded, checkFeatureAccess } = useBilling()
if (!loaded) {
return null // Content disappears!
}
return <DashboardContent />
}
Correct: show skeleton matching content layout
function Dashboard() {
const { loaded, checkFeatureAccess } = useBilling()
if (!loaded || !checkFeatureAccess) {
return (
<div className="space-y-4">
<div className="h-8 w-48 bg-gray-200 animate-pulse rounded" />
<div className="h-64 bg-gray-200 animate-pulse rounded" />
</div>
)
}
return <DashboardContent />
}
2. Server-Side Gating
Impact: HIGH
Client-side feature checks are for UI purposes only. Any sensitive operation or data access must verify subscription status server-side. Users can bypass client-side checks by modifying frontend code or using browser developer tools.
2.1 Verify Access on Server
Impact: HIGH (security requirement)
Never trust client-side access checks for operations that cost money, access sensitive data, or perform privileged actions.
Incorrect: trusts client-side check for sensitive operation
// API route
export async function POST(req: Request) {
// Client could bypass this by modifying frontend code
const { hasAccess } = await req.json()
if (!hasAccess) {
return Response.json({ error: 'No access' }, { status: 403 })
}
return performSensitiveOperation()
}
Correct: verify server-side
// API route
import { flowglad } from '@/lib/flowglad'
import { auth } from '@/lib/auth'
export async function POST(req: Request) {
const session = await auth()
if (!session?.user?.id) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
const billing = await flowglad(session.user.id).getBilling()
if (!billing.checkFeatureAccess('api-access')) {
return Response.json({ error: 'Upgrade required' }, { status: 403 })
}
return performSensitiveOperation()
}
2.2 API Route Protection
Impact: HIGH (prevents unauthorized access)
Create a reusable pattern for protecting multiple API routes with feature checks.
Incorrect: duplicates check logic everywhere
// routes/generate.ts
export async function POST(req: Request) {
const session = await auth()
const billing = await flowglad(session.user.id).getBilling()
if (!billing.checkFeatureAccess('ai-generation')) {
return Response.json({ error: 'Upgrade required' }, { status: 403 })
}
// ... generation logic
}
// routes/export.ts
export async function POST(req: Request) {
const session = await auth()
const billing = await flowglad(session.user.id).getBilling()
if (!billing.checkFeatureAccess('export')) {
return Response.json({ error: 'Upgrade required' }, { status: 403 })
}
// ... export logic
}
Correct: create reusable middleware/helper
// lib/requireFeature.ts
import { flowglad } from '@/lib/flowglad'
import { auth } from '@/lib/auth'
export async function requireFeature(featureSlug: string) {
const session = await auth()
if (!session?.user?.id) {
return { error: 'Unauthorized', status: 401 }
}
const billing = await flowglad(session.user.id).getBilling()
if (!billing.checkFeatureAccess(featureSlug)) {
return { error: 'Upgrade required', status: 403 }
}
return { userId: session.user.id, billing }
}
// routes/generate.ts
export async function POST(req: Request) {
const result = await requireFeature('ai-generation')
if ('error' in result) {
return Response.json({ error: result.error }, { status: result.status })
}
const { userId, billing } = result
// ... generation logic
}
3. Feature Identification
Impact: MEDIUM
How you reference features affects code maintainability and environment portability.
3.1 Use Slugs Not IDs
Impact: MEDIUM (environment portability)
Feature IDs are auto-generated and differ between development, staging, and production environments. Slugs are stable identifiers you control.
Incorrect: hardcoding Flowglad IDs
// IDs change between environments!
if (billing.checkFeatureAccess('feat_abc123xyz')) {
// Works in dev, breaks in production
}
Correct: use slugs
// Slugs are stable across environments
if (billing.checkFeatureAccess('advanced-analytics')) {
// Works everywhere
}
Define feature slugs in your Flowglad dashboard and reference them consistently in code.
4. Component Wrapper Patterns
Impact: MEDIUM
Reusable patterns for gating components reduce boilerplate and ensure consistent behavior.
4.1 Feature Gate Component
Impact: MEDIUM (reduces boilerplate)
Create a declarative component for gating content.
Incorrect: repeats gate logic in every component
function AnalyticsDashboard() {
const { loaded, checkFeatureAccess } = useBilling()
if (!loaded || !checkFeatureAccess) return <Skeleton />
if (!checkFeatureAccess('analytics')) return <UpgradePrompt feature="analytics" />
return <Analytics />
}
function ExportButton() {
const { loaded, checkFeatureAccess } = useBilling()
if (!loaded || !checkFeatureAccess) return <Skeleton />
if (!checkFeatureAccess('export')) return <UpgradePrompt feature="export" />
return <ExportUI />
}
Correct: create reusable FeatureGate component
// components/FeatureGate.tsx
import { useBilling } from '@flowglad/nextjs'
import { ReactNode } from 'react'
interface FeatureGateProps {
feature: string
children: ReactNode
fallback?: ReactNode
loading?: ReactNode
}
export function FeatureGate({
feature,
children,
fallback = <UpgradePrompt />,
loading = <Skeleton />,
}: FeatureGateProps) {
const { loaded, checkFeatureAccess } = useBilling()
if (!loaded || !checkFeatureAccess) {
return <>{loading}</>
}
if (!checkFeatureAccess(feature)) {
return <>{fallback}</>
}
return <>{children}</>
}
// Usage
function AnalyticsDashboard() {
return (
<FeatureGate feature="analytics">
<Analytics />
</FeatureGate>
)
}
function ExportButton() {
return (
<FeatureGate feature="export" fallback={<LockedExportButton />}>
<ExportUI />
</FeatureGate>
)
}
4.2 Higher-Order Component Pattern
Impact: MEDIUM (alternative pattern for class components or full-page gates)
Use HOC pattern when you need to gate entire pages or components.
Incorrect: duplicates page-level checks
// pages/analytics.tsx
export default function AnalyticsPage() {
const { loaded, checkFeatureAccess } = useBilling()
if (!loaded || !checkFeatureAccess) return <PageSkeleton />
if (!checkFeatureAccess('analytics')) {
// Using redirect() in a client
---
*Content truncated.*
More by flowglad
View all skills by flowglad →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversTheta Health MCP Server offers EHR interoperability solutions, enabling AI assistants to access and manage diverse healt
Unlock AI-ready web data with Firecrawl: scrape any website, handle dynamic content, and automate web scraping for resea
Easily access and modify notes in your Obsidian vaults using a REST API. Integrate Obsidian features seamlessly into you
Connect Supabase projects to AI with Supabase MCP Server. Standardize LLM communication for secure, efficient developmen
AppleScript MCP server lets AI execute apple script on macOS, accessing Notes, Calendar, Contacts, Messages & Finder via
Inspect schemas and run SQL queries quickly using BigQuery. Access Google BigQuery data with interactive features and ea
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.