Skip to content

User Maturity

User maturity is an automatic classification of users into behavioral segments. Instead of manually segmenting users, Presage computes maturity from behavioral signals.

SegmentDescriptionDefault threshold
newJust getting started3 or fewer sessions
onboardingLearning the ropes4-10 sessions
activeRegular user past onboardingMore than 10 sessions, fewer than 5 features used
powerHeavy feature adopter5+ distinct features used
dormantHas gone quiet14+ days since last seen

The computation follows this priority order:

  1. Dormant check — If the user has not been seen for dormantDaysInactive days, they are dormant (regardless of other signals)
  2. New check — If sessionCount is at or below newMaxSessions, they are new
  3. Power check — If the number of distinct features used is at or above powerMinFeatures, they are power
  4. Onboarding check — If sessionCount is at or below onboardingMaxSessions, they are onboarding
  5. Fallback — Otherwise, they are active
// Simplified version of the actual computation
function computeMaturity(signals: BehavioralSignals, config: MaturityConfig): Maturity {
const daysSinceLastSeen = daysBetween(new Date(signals.lastSeenAt), new Date())
if (daysSinceLastSeen >= config.dormantDaysInactive) return 'dormant'
if (signals.sessionCount <= config.newMaxSessions) return 'new'
const featuresUsed = Object.keys(signals.featureUsage).length
if (featuresUsed >= config.powerMinFeatures) return 'power'
if (signals.sessionCount <= config.onboardingMaxSessions) return 'onboarding'
return 'active'
}

Override any threshold via the maturity config:

const client = createAdaptiveClient({
maturity: {
newMaxSessions: 5, // Consider "new" for 5 sessions instead of 3
onboardingMaxSessions: 15, // Extend onboarding period
powerMinFeatures: 8, // Require more features for "power" status
dormantDaysInactive: 30, // Wait longer before marking as dormant
},
// ...rules, persistence
})

The full config interface:

interface MaturityConfig {
newMaxSessions: number // Default: 3
onboardingMaxSessions: number // Default: 10
powerMinFeatures: number // Default: 5
dormantDaysInactive: number // Default: 14
}

Maturity is accessible at the top level of the context (not nested under traits or signals):

{
id: 'new-user-onboarding',
adaptationId: 'onboarding',
priority: 10,
conditions: {
all: [
{ field: 'maturity', operator: 'eq', value: 'new' },
],
},
action: { type: 'show', variantId: 'guided-tour' },
}

You can combine maturity with other conditions:

{
id: 'dormant-enterprise-reactivation',
adaptationId: 'banner',
priority: 20,
conditions: {
all: [
{ field: 'maturity', operator: 'eq', value: 'dormant' },
{ field: 'traits.plan', operator: 'eq', value: 'enterprise' },
],
},
action: { type: 'show', variantId: 'reactivation-offer' },
}

Example: Different Onboarding for Each Maturity Level

Section titled “Example: Different Onboarding for Each Maturity Level”
import { createAdaptiveClient, createLocalStorageDriver } from '@presage-kit/core'
const client = createAdaptiveClient({
rules: [
{
id: 'new-guided-tour',
adaptationId: 'onboarding',
priority: 50,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'new' }],
},
action: { type: 'show', variantId: 'guided-tour' },
},
{
id: 'onboarding-tips',
adaptationId: 'onboarding',
priority: 40,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'onboarding' }],
},
action: { type: 'show', variantId: 'contextual-tips' },
},
{
id: 'active-whats-new',
adaptationId: 'onboarding',
priority: 30,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'active' }],
},
action: { type: 'show', variantId: 'whats-new' },
},
{
id: 'power-changelog',
adaptationId: 'onboarding',
priority: 20,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'power' }],
},
action: { type: 'show', variantId: 'changelog-only' },
},
{
id: 'dormant-reengagement',
adaptationId: 'onboarding',
priority: 60,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'dormant' }],
},
action: { type: 'show', variantId: 'welcome-back' },
},
],
persistence: {
driver: createLocalStorageDriver('my-app'),
},
})

Then in your React component:

import { Adaptive, Variant } from '@presage-kit/react'
function OnboardingFlow() {
return (
<Adaptive id="onboarding" defaultVariant="contextual-tips">
<Variant id="guided-tour">
<GuidedTour />
</Variant>
<Variant id="contextual-tips">
<ContextualTips />
</Variant>
<Variant id="whats-new">
<WhatsNew />
</Variant>
<Variant id="changelog-only">
<Changelog />
</Variant>
<Variant id="welcome-back">
<WelcomeBack />
</Variant>
</Adaptive>
)
}

Maturity is recomputed every time signals change (after each track() call). This means a user can transition between segments within a single session:

  1. User arrives (session 1) → new
  2. User returns for session 4 → onboarding
  3. User uses 5 distinct features → power
  4. User disappears for 14 days → dormant

Your UI adapts at each transition without any manual intervention.