Documentation Index
Fetch the complete documentation index at: https://docs.encorekit.com/llms.txt
Use this file to discover all available pages before exploring further.
Returns a fluent builder for presenting offers with chainable callback methods. Provides an expressive alternative to presentOffer().
Signature
function placement(): PlacementBuilder
Return Value
Type: PlacementBuilder
interface PlacementBuilder {
onGranted(callback: (entitlement: EntitlementType) => void): PlacementBuilder
onNotGranted(callback: (reason: NotGrantedReason) => void): PlacementBuilder
onLoadingStateChange(callback: (isLoading: boolean) => void): PlacementBuilder
show(): Promise<PresentationResult>
}
PlacementBuilder Methods
Set callback for when entitlement is grantedonGranted(callback: (entitlement: EntitlementType) => void): PlacementBuilder
Returns: PlacementBuilder for chaining
Set callback for when entitlement is not grantedonNotGranted(callback: (reason: NotGrantedReason) => void): PlacementBuilder
Returns: PlacementBuilder for chaining
Set callback for loading state changesonLoadingStateChange(callback: (isLoading: boolean) => void): PlacementBuilder
Returns: PlacementBuilder for chaining
Display the offer modalshow(): Promise<PresentationResult>
Returns: Promise<PresentationResult>
Examples
Basic Usage
import Encore from '@encorekit/web-sdk';
const result = await Encore.placement()
.onGranted((entitlement) => {
console.log('Granted:', entitlement);
})
.onNotGranted((reason) => {
console.log('Not granted:', reason);
})
.show();
With Loading State
const result = await Encore.placement()
.onLoadingStateChange((isLoading) => {
setLoading(isLoading);
})
.onGranted((entitlement) => {
unlockFeature();
trackEvent('offer_accepted');
})
.onNotGranted((reason) => {
if (reason === 'noOffersAvailable') {
showAlternativeFlow();
}
})
.show();
Complete Example
import Encore from '@encorekit/web-sdk';
async function presentOfferWithCallbacks() {
const result = await Encore.placement()
.onLoadingStateChange((isLoading) => {
document.getElementById('btn').disabled = isLoading;
document.getElementById('btn').textContent =
isLoading ? 'Loading...' : 'Get Premium';
})
.onGranted((entitlement) => {
console.log('User granted:', entitlement);
showSuccessMessage('Premium access unlocked!');
unlockPremiumFeatures();
analytics.track('offer_accepted', {
type: entitlement.type
});
})
.onNotGranted((reason) => {
console.log('Not granted:', reason);
if (reason === 'noOffersAvailable') {
showMessage('No offers available right now');
} else if (reason === 'userDeclinedLastOffer') {
showMessage('Check out our pricing plans');
redirectToPricing();
}
analytics.track('offer_declined', { reason });
})
.show();
return result;
}
Framework Integration
import { useState } from 'react';
import Encore from '@encorekit/web-sdk';
function OfferButton() {
const [loading, setLoading] = useState(false);
const handleClick = async () => {
await Encore.placement()
.onLoadingStateChange(setLoading)
.onGranted((entitlement) => {
console.log('Granted:', entitlement);
})
.onNotGranted((reason) => {
console.log('Not granted:', reason);
})
.show();
};
return (
<button onClick={handleClick} disabled={loading}>
{loading ? 'Loading...' : 'Get Premium'}
</button>
);
}
<template>
<button @click="handleClick" :disabled="loading">
{{ loading ? 'Loading...' : 'Get Premium' }}
</button>
</template>
<script setup>
import { ref } from 'vue';
import Encore from '@encorekit/web-sdk';
const loading = ref(false);
async function handleClick() {
await Encore.placement()
.onLoadingStateChange((isLoading) => {
loading.value = isLoading;
})
.onGranted((entitlement) => {
console.log('Granted:', entitlement);
})
.onNotGranted((reason) => {
console.log('Not granted:', reason);
})
.show();
}
</script>
import { Component } from '@angular/core';
import Encore from '@encorekit/web-sdk';
@Component({
selector: 'app-offer-button',
template: `
<button (click)="handleClick()" [disabled]="loading">
{{ loading ? 'Loading...' : 'Get Premium' }}
</button>
`
})
export class OfferButtonComponent {
loading = false;
async handleClick() {
await Encore.placement()
.onLoadingStateChange((isLoading) => {
this.loading = isLoading;
})
.onGranted((entitlement) => {
console.log('Granted:', entitlement);
})
.onNotGranted((reason) => {
console.log('Not granted:', reason);
})
.show();
}
}
Comparison with presentOffer()
Both methods are equivalent, choose based on your preference:
placement() - Fluent
presentOffer() - Options
// Fluent, chainable style
await Encore.placement()
.onGranted(handleGranted)
.onNotGranted(handleNotGranted)
.show();
// Options object style
await Encore.presentOffer({
onGranted: handleGranted,
onNotGranted: handleNotGranted
});
Callback Execution
Callbacks are executed in this order:
onLoadingStateChange(true) - When modal starts loading
onGranted() OR onNotGranted() - Based on result
onLoadingStateChange(false) - When modal closes
await Encore.placement()
.onLoadingStateChange((isLoading) => {
console.log('Loading:', isLoading);
// Called twice: true (start), false (end)
})
.onGranted((entitlement) => {
console.log('Granted:', entitlement);
// Only if user claims offer
})
.onNotGranted((reason) => {
console.log('Not granted:', reason);
// Only if user doesn't claim offer
})
.show();
Method Chaining
All builder methods return this, allowing method chaining:
// All methods can be chained
Encore.placement()
.onGranted(callback1)
.onNotGranted(callback2)
.onLoadingStateChange(callback3)
.show();
// Order doesn't matter (except .show() must be last)
Encore.placement()
.onLoadingStateChange(callback3)
.onGranted(callback1)
.onNotGranted(callback2)
.show();
Best Practices
1. Always Call show()
The builder doesn’t present anything until show() is called:
// Good
await Encore.placement()
.onGranted(handler)
.show(); // ✅ Presents modal
// Doesn't work
Encore.placement()
.onGranted(handler); // ❌ Nothing happens
2. Use Loading State Callback
Provide feedback during async operations:
// Good
Encore.placement()
.onLoadingStateChange(setLoading) // ✅ Shows loading state
.show();
3. Handle All Outcomes
// Good
Encore.placement()
.onGranted(handleSuccess)
.onNotGranted(handleDecline) // ✅ Handles both cases
.show();
4. Callbacks Are Optional
You can use the builder without callbacks:
// Still works - just uses the promise result
const result = await Encore.placement().show();
if (result.granted) {
// Handle result
}
Complete Example
class OfferManager {
async showOffer() {
const result = await Encore.placement()
.onLoadingStateChange((isLoading) => {
this.updateLoadingUI(isLoading);
})
.onGranted((entitlement) => {
this.handleGranted(entitlement);
})
.onNotGranted((reason) => {
this.handleNotGranted(reason);
})
.show();
return result;
}
updateLoadingUI(isLoading) {
const button = document.getElementById('offerBtn');
button.disabled = isLoading;
button.textContent = isLoading ? 'Loading...' : 'Get Premium';
}
handleGranted(entitlement) {
console.log('Access granted:', entitlement);
// Update UI
this.showSuccessMessage('Premium unlocked!');
this.unlockFeatures();
// Track analytics
this.trackEvent('offer_accepted', {
entitlementType: entitlement.type,
value: entitlement.value
});
}
handleNotGranted(reason) {
console.log('Not granted:', reason);
if (reason === 'noOffersAvailable') {
this.showMessage('No offers available');
} else if (reason === 'userDeclinedLastOffer') {
this.showPricingAlternative();
}
// Track analytics
this.trackEvent('offer_declined', { reason });
}
}
Next Steps