Every user who installs your app has an intent. Someone who tapped an ad for running shoes wants to see running shoes. Someone who followed a friend's referral wants to know what their friend uses the app for. Someone who scanned a QR code at a restaurant wants to see the menu. Contextual onboarding reads this intent from the deep link data and adapts accordingly.
For personalization strategies, see Personalized Onboarding Flows with Deep Link Data. For invite-specific flows, see Invite Link Onboarding: From Invitation to Active User.
Intent Signals in Deep Links
What Deep Links Tell You
Every deep link carries intent signals, even if they're implicit. For a comprehensive look at how contextual deep linking works under the hood, see the Contextual Deep Linking Guide.
| Signal | Example Parameter | Implied Intent |
|---|---|---|
| Product category | category=running |
Wants running products |
| Specific item | product_id=abc123 |
Wants to see this product |
| Referrer | ref=sarah_123 |
Trusts the referrer |
| Campaign | utm_campaign=summer_sale |
Interested in deals |
| Feature | feature=workout_tracker |
Wants this specific capability |
| Location context | store=downtown_branch |
In-store shopper |
| Content type | content=recipe_collection |
Interested in recipes |
Capturing Intent
async function captureUserIntent() {
const deferred = await Tolinku.checkDeferredLink();
if (deferred === null) {
return { type: 'organic', confidence: 'low' };
}
const params = deferred.params;
const path = deferred.path;
// Determine intent type and confidence
if (params.product_id) {
return {
type: 'product_specific',
confidence: 'high',
product: params.product_id,
source: params.utm_source,
};
}
if (params.category) {
return {
type: 'category_interest',
confidence: 'medium',
category: params.category,
source: params.utm_source,
};
}
if (params.ref) {
return {
type: 'referral',
confidence: 'high',
referrer: params.ref,
referrerName: params.referrer_name,
};
}
if (params.utm_campaign) {
return {
type: 'campaign',
confidence: 'medium',
campaign: params.utm_campaign,
source: params.utm_source,
};
}
return {
type: 'deep_link_generic',
confidence: 'low',
path: path,
};
}
Adapting the Flow
Intent-Based Routing
function OnboardingRouter({ intent }) {
switch (intent.type) {
case 'product_specific':
return <ProductFocusedOnboarding intent={intent} />;
case 'category_interest':
return <CategoryOnboarding intent={intent} />;
case 'referral':
return <ReferralOnboarding intent={intent} />;
case 'campaign':
return <CampaignOnboarding intent={intent} />;
case 'organic':
default:
return <StandardOnboarding />;
}
}
Product-Specific Intent
The user clicked a link to a specific product. Show it immediately, then onboard around it:
function ProductFocusedOnboarding({ intent }) {
const [phase, setPhase] = useState('product_view');
return (
<View>
{phase === 'product_view' && (
<ProductPreview
productId={intent.product}
onContinue={() => setPhase('signup')}
ctaText="Sign up to purchase"
/>
)}
{phase === 'signup' && (
<QuickSignup
context={`Sign up to buy ${intent.productName}`}
onComplete={() => setPhase('purchase')}
/>
)}
{phase === 'purchase' && (
<ProductPage
productId={intent.product}
showPromoBanner={intent.promoCode ? true : false}
/>
)}
</View>
);
}
This flow is: show value, then ask for commitment. The product view gives the user a reason to sign up.
Category Interest
The user came from an ad or content about a specific category. Pre-select their interest:
function CategoryOnboarding({ intent }) {
return (
<View>
<WelcomeScreen
headline={getCategoryWelcome(intent.category)}
subtext="Let's set up your experience."
/>
<AccountCreation />
{/* Skip "What are you interested in?" — we already know */}
<CategoryFeed
category={intent.category}
preSelected={true}
/>
</View>
);
}
function getCategoryWelcome(category) {
const welcomes = {
running: 'Ready to find your perfect running gear?',
cooking: 'Let us help you discover new recipes.',
fitness: 'Your fitness journey starts here.',
travel: 'Where are you headed next?',
};
return welcomes[category] || `Welcome! Let's get you started.`;
}
Campaign Context
Users from marketing campaigns expect to see what was advertised:
function CampaignOnboarding({ intent }) {
const campaign = getCampaignConfig(intent.campaign);
return (
<View>
<CampaignLanding
headline={campaign.headline}
heroImage={campaign.image}
ctaText={campaign.cta}
/>
<AccountCreation promoCode={campaign.promoCode} />
{/* Navigate to the campaign-specific content */}
<Redirect to={campaign.destinationScreen} />
</View>
);
}
function getCampaignConfig(campaignId) {
const campaigns = {
summer_sale: {
headline: 'Summer Sale: Up to 50% Off',
image: 'summer-sale-hero.jpg',
cta: 'Shop the Sale',
promoCode: 'SUMMER50',
destinationScreen: '/sale/summer',
},
new_feature: {
headline: 'Introducing Workout Tracking',
image: 'workout-tracking-hero.jpg',
cta: 'Try It Free',
promoCode: null,
destinationScreen: '/feature/workout-tracking',
},
};
return campaigns[campaignId] || { headline: 'Welcome', cta: 'Get Started' };
}
Context Confidence Levels
Not all context is equally reliable. Handle uncertainty:
High Confidence (Act on It)
- Specific product ID in the URL
- Referrer ID with valid account
- Known campaign with active config
These are safe to use for onboarding customization. Show the product, acknowledge the referrer, display the campaign offer.
Medium Confidence (Suggest, Don't Assume)
- Category from UTM parameters
- Feature interest from content link
- Location from QR code
Use these to pre-select options or reorder content, but let the user change. For a reference on which parameters to include in your links, see Onboarding Deep Link Parameters.
function InterestPicker({ suggestedCategory }) {
const [selected, setSelected] = useState(suggestedCategory);
return (
<View>
{suggestedCategory && (
<Text>Based on how you found us, we think you're interested in:</Text>
)}
<CategoryGrid
categories={allCategories}
selected={selected}
onSelect={setSelected}
highlighted={suggestedCategory}
/>
</View>
);
}
Low Confidence (Fall Back to Default)
- No deep link data (organic install)
- Deep link with only UTM source (no specific content)
- Expired or invalid deep link
Use the standard onboarding flow.
Contextual Permission Requests
Tie permission requests to the user's intent:
function getPermissionContext(intent) {
switch (intent.type) {
case 'product_specific':
return {
push: 'Get notified when this item goes on sale or ships.',
location: 'Find stores near you that carry this item.',
};
case 'category_interest':
return {
push: `Get alerts for new ${intent.category} arrivals and deals.`,
location: null, // Not relevant
};
case 'referral':
return {
push: `Get notified when ${intent.referrerName} shares something new.`,
location: null,
};
default:
return {
push: 'Get notified about updates and personalized recommendations.',
location: 'Find relevant content near you.',
};
}
}
Contextual permission explanations have higher grant rates because the user understands the specific benefit.
Handling Context Across Sessions
Persisting Context
Store the intent data so it can influence the experience beyond the first session:
async function persistIntent(userId, intent) {
await userPreferences.set(userId, {
initialIntent: intent,
primaryCategory: intent.category,
acquisitionSource: intent.source,
referrer: intent.referrer,
capturedAt: Date.now(),
});
}
Using Context After Onboarding
The deep link context is valuable beyond onboarding:
- Home feed: Prioritize content from the user's initial category
- Recommendations: Weight suggestions toward the initial interest
- Notifications: Reference the initial context ("More items like the one you first looked at")
- Re-engagement: If the user churns, remind them of their original interest
Measuring Contextual Onboarding
Compare Against Standard
async function contextualOnboardingReport(dateRange) {
const segments = [
{ name: 'Organic (no context)', filter: { intentType: 'organic' } },
{ name: 'Product-specific', filter: { intentType: 'product_specific' } },
{ name: 'Category interest', filter: { intentType: 'category_interest' } },
{ name: 'Referral', filter: { intentType: 'referral' } },
{ name: 'Campaign', filter: { intentType: 'campaign' } },
];
for (const seg of segments) {
const completion = await getCompletionRate(dateRange, seg.filter);
const activation = await getActivationRate(dateRange, seg.filter);
const retention = await getDay7Retention(dateRange, seg.filter);
console.log(seg.name, {
completion: completion.toFixed(1) + '%',
activation: activation.toFixed(1) + '%',
day7Retention: retention.toFixed(1) + '%',
});
}
}
Expected Results
| Segment | Completion | Activation | Day 7 Retention |
|---|---|---|---|
| Organic | 42% | 18% | 12% |
| Product-specific | 58% | 35% | 22% |
| Category interest | 52% | 28% | 18% |
| Referral | 68% | 42% | 30% |
| Campaign | 48% | 22% | 14% |
Product-specific and referral contexts produce the highest-quality users because the intent is clearest.
For deferred deep linking, see the deferred deep linking docs. For deep linking features, see Tolinku deep linking. For onboarding use cases, see the onboarding documentation.
Get deep linking tips in your inbox
One email per week. No spam.