BibleWeb gates advanced features behind paid tiers. The tier system is the single source of truth for what each user can access. It must be usable both in server-side route guards and in client-side UI to show or hide features and prompt upgrades where appropriate.
Three tiers are defined: free, pro, premium.
Each tier maps to a TierFeatures record:
| Feature key | Type | Free | Pro | Premium |
|---|---|---|---|---|
maxNotes |
number | null |
5 | unlimited | unlimited |
dutchTranslation |
boolean |
true | true | true |
parallelGospel |
boolean |
true | true | true |
interlinear |
boolean |
false | true | true |
commentaries |
boolean |
false | true | true |
crossRefGraph |
boolean |
false | true | true |
offlineDownload |
boolean |
false | true | true |
noteCrossLinking |
boolean |
false | true | true |
noteExport |
boolean |
false | false | true |
aiChat |
boolean |
false | false | true |
personalTranslation |
boolean |
false | false | true |
null for maxNotes means unlimited. hasFeature() treats null and true as positive, false and 0 as negative.
Beta tier system (separate but related — see beta-activation-system):
beta_high, beta_low, admin) sit alongside free/pro/premium and are resolved by getBetaTier() in beta-codes.ts.TierFeatures system. The two systems are currently independent./pricing) is the primary surface for communicating tier differences — see the pricing-page feature for the full presentation.hasFeature(tier, key) to decide whether to render a feature or show an upgrade prompt.TierGate component exists yet — gating is expected to be implemented per-feature using the exported helpers directly.Core module: apps/web/src/lib/tier.ts
Exports:
type Tier = 'free' | 'pro' | 'premium'
interface TierFeatures {
maxNotes: number | null;
dutchTranslation: boolean;
parallelGospel: boolean;
interlinear: boolean;
commentaries: boolean;
crossRefGraph: boolean;
offlineDownload: boolean;
aiChat: boolean;
personalTranslation: boolean;
noteExport: boolean;
noteCrossLinking: boolean;
}
function getTierFeatures(tier: Tier): TierFeatures
function hasFeature(tier: Tier, feature: keyof TierFeatures): boolean
hasFeature logic: returns true if the feature value is true, null, or a number > 0.
Usage pattern:
hasFeature and the user's current tier (from user.currentTier in the user store).hasFeature(tier, 'interlinear') etc. to conditionally render UI or redirect to /pricing.TierFeatures type is imported by the pricing page to render the comparison table, ensuring the UI and the logic stay in sync.Current tier source:
app_metadata.beta_tier.'free' (or beta tier from cookie, handled by beta system).user.currentTier always resolves to free for non-beta users in the current build.The tier definitions and helper functions are fully implemented. The pricing page correctly reflects the matrix. Per-feature gating in the reader/notes UI needs to be connected — hasFeature() is ready to use but individual features may not yet be checking it. Stripe integration (actual paid tier assignment) is not yet built.