Content Management
Content management allows you to discover, analyze, and control access to content on your pages. The SDK automatically finds content based on configured selectors and provides methods to check access and unlock premium content.
📘 See Also: Content Metadata & Fallback Mechanism for detailed information about meta tags, element attributes, and best practices.
How It Works
- Configuration - Define content selectors in your config or via Scripts Host
- Discovery - SDK scans the page for matching content elements
- Metadata Extraction - Extracts metadata using a multi-layered fallback approach (configured selectors → element attributes → meta tags)
- Access Control - Integrates with entitlements to control access
- Content Unlocking - Fetches and decrypts premium content when authorized
Methods
content.list()
Discover all content items on the current page.
Returns: ContentItem[]
const articles = window.sesamy.content.list();
articles.forEach((article) => {
console.log('Found:', article.title);
console.log('Price:', article.price);
console.log('Requires:', article.pass);
});content.get()
Get a specific content item by selector or element.
Parameters: elementOrSelector (HTMLElement | string)
Returns: ContentItem | null
const article = window.sesamy.content.get('article.featured');
if (article) {
console.log('Article:', article.title);
}content.hasAccess()
Check if user has access to content.
Parameters: elementOrSelector (HTMLElement | string)
Returns: Promise<Entitlement | boolean | null>
const result = await window.sesamy.content.hasAccess('.premium-article');
if (result) {
if (typeof result === 'boolean') {
// User has access (public or logged-in content)
console.log('Access granted');
} else {
// User has access via entitlement
console.log('Access granted via:', result.type);
}
} else {
// User doesn't have access - show paywall
const article = window.sesamy.content.get('.premium-article');
showPaywall(article.paywallUrl);
}The function returns:
- An
Entitlementobject if the user has entitlement-based access trueif the user has access (public content or authenticated for login-only content)nullif the user doesn't have access
content.unlock()
Unlock and retrieve premium content.
Parameters: elementOrSelector (HTMLElement | string)
Returns: Promise<string | null>
const unlockedHTML = await window.sesamy.content.unlock('#article');
if (unlockedHTML) {
document.querySelector('#content').innerHTML = unlockedHTML;
} else {
// User doesn't have access
showPaywall();
}Configuration
Configure content selectors to define how the SDK discovers and extracts metadata:
{
content: [
{
type: 'article',
path: '/articles', // Optional: only match on this path
queryParam: { key: 'param', value: 'value' }, // Optional: match on query parameter
headers: { name: 'User-Agent', contains: 'app-name' }, // Optional: match on header (substring, case-insensitive)
pass: 'premium', // Optional: required pass
price: {
amount: 9.99,
currency: 'USD',
},
enablePaywallSettingsUrlFallback: false, // Optional: Fallback to <sesamy-paywall settings-url>
selectors: {
article: { selector: 'article.content' },
title: { selector: 'h1', attribute: 'textContent' },
image: { selector: 'img', attribute: 'src' },
author: { selector: '.author', attribute: 'textContent' },
paywallUrl: { selector: 'a.paywall-link', attribute: 'href' },
// ... more selectors
},
},
];
}Content Matching
The SDK supports multiple ways to match content configurations to specific pages:
Path Matching
Match when the URL path contains a specific string:
{
type: 'article',
path: '/premium', // Matches /premium, /premium/article, etc.
selectors: { /* ... */ }
}Query Parameter Matching
Match when a specific query parameter has an exact value:
{
type: 'article',
queryParam: { key: 'preview', value: 'true' }, // Matches ?preview=true
selectors: { /* ... */ }
}Header Matching
Match when a header contains a specific value (case-insensitive substring match):
{
type: 'article',
headers: { name: 'User-Agent', contains: 'mobile-app' }, // Matches User-Agent containing "mobile-app"
selectors: { /* ... */ }
}Browser Limitation
In browser environments, only the User-Agent header is accessible via navigator.userAgent. Other headers will not match in client-side code.
Multiple Conditions
All conditions specified on a content node must match. Place more specific configurations first:
content: [
{
type: 'article',
path: '/articles',
headers: { name: 'User-Agent', contains: 'mobile-app' },
selectors: {
/* mobile app specific selectors */
},
},
{
type: 'article',
path: '/articles',
selectors: {
/* default article selectors */
},
},
];Priority Order
Content nodes are matched in order. The first node where all conditions match is used:
- Check
pathcondition (if specified) - Check
queryParamcondition (if specified) - Check
headerscondition (if specified) - If all conditions pass (or no conditions specified), use this node
Best Practice
Place content nodes with more specific conditions (multiple filters) at the top of the array, and generic/fallback configurations at the bottom.
Pass Collection
The pass attribute is collected from the article element and all its ancestor elements up to the document root. This allows for flexible, hierarchical access control:
<body pass="https://example.com/site-wide">
<main pass="https://example.com/members">
<section pass="https://example.com/premium">
<sesamy-article pass="https://example.com/article-123">
<!-- All four passes are collected and checked -->
</sesamy-article>
</section>
</main>
</body>How It Works
When retrieving content with content.get() or content.list(), the SDK:
- Starts at the article element
- Collects any
passattributes - Traverses up through parent elements
- Collects all
passattributes from ancestors - Splits comma-separated values
- Removes duplicates
- Combines into a single comma-separated string
Use Cases
Section-based access:
<section class="members-only" pass="https://example.com/members">
<sesamy-article>Article 1</sesamy-article>
<sesamy-article>Article 2</sesamy-article>
<!-- Both articles inherit the members pass -->
</section>Dynamic passes from CMS:
<!-- Lab plugin adds passes dynamically based on tags -->
<section class="article" pass="https://example.com/tag1,https://example.com/tag2">
<sesamy-article>
<!-- Inherits tag-based passes -->
</sesamy-article>
</section>Multiple access levels:
<article pass="https://example.com/basic">
<div pass="https://example.com/premium">
<sesamy-article>
<!-- Requires either basic OR premium pass -->
</sesamy-article>
</div>
</article>Best Practices
- Place site-wide passes on
<body>or high-level containers - Use section/category passes on section elements
- Use article-specific passes on
<sesamy-article>elements - Let plugins like Labrador Tags handle dynamic passes
Common Patterns
Content List with Access Check
window.addEventListener('sesamyJsReady', async () => {
const articles = window.sesamy.content.list();
for (const article of articles) {
const entitlement = await window.sesamy.content.hasAccess(article.element);
if (!entitlement) {
// Add paywall overlay
const overlay = document.createElement('div');
overlay.className = 'paywall-overlay';
overlay.innerHTML = `
<h3>Premium Content</h3>
<p>Subscribe to read this article</p>
<button onclick="location.href='${article.paywallUrl}'">
Subscribe Now
</button>
`;
article.element.appendChild(overlay);
}
}
});Dynamic Content Unlocking
async function unlockArticle(selector) {
const entitlement = await window.sesamy.content.hasAccess(selector);
if (entitlement) {
const content = await window.sesamy.content.unlock(selector);
if (content) {
document.querySelector('.article-body').innerHTML = content;
document.querySelector('.paywall').remove();
// Track unlock event
await window.sesamy.analytics.track('content_unlocked', {
contentId: entitlement.publisherContentId,
});
}
} else {
// Show paywall with article info
const article = window.sesamy.content.get(selector);
showPaywall({
title: article.title,
price: article.price,
paywallUrl: article.paywallUrl,
});
}
}Paywall with Purchase Flow
window.addEventListener('sesamyJsReady', async () => {
const article = window.sesamy.content.get('article.main');
const access = await window.sesamy.content.hasAccess(article.element);
if (!access) {
document.querySelector('#purchase-btn').addEventListener('click', async () => {
const checkout = await window.sesamy.checkouts.create({
items: [
{
url: article.url,
price: article.price.amount,
currency: article.price.currency,
},
],
redirectUrl: window.location.href,
});
window.location.href = checkout.checkoutUrl;
});
}
});Next Steps
- Content Metadata & Fallback Mechanism - Learn about meta tags, attributes, and best practices
- API Reference - Use entitlements with content
- Authentication - Manage user sessions
- Scripts Host - Automatic configuration
Modals & Notification Bars
The SDK provides helpers for displaying modal dialogs and notification bars. These are useful for showing custom UI like forms, alerts, or banners—typically containing web components.
content.showModal()
Display a centered modal overlay with custom content.
Parameters:
content(string | Element) - HTML string or DOM element to displayoptions(ModalOptions) - Optional configuration
Returns: ModalResult with element and close() function
// Show a modal with an HTML string
const { close } = window.sesamy.content.showModal(`
<my-custom-form></my-custom-form>
`);
// Show a modal with a DOM element
const form = document.createElement('my-signup-form');
const modal = window.sesamy.content.showModal(form);
// Close programmatically
modal.close();Options:
| Option | Type | Default | Description |
|---|---|---|---|
closeOnBackdropClick | boolean | true | Close when clicking outside the content |
closeOnEscape | boolean | true | Close when pressing Escape key |
zIndex | number | 2147483647 | CSS z-index for the overlay |
backdropBlur | string | '5px' | Blur amount for backdrop filter |
overlayBackground | string | 'rgba(0,0,0,0.5)' | Background color of overlay |
// Modal that requires explicit close action
const { close } = window.sesamy.content.showModal(formElement, {
closeOnBackdropClick: false,
closeOnEscape: false
});content.showNotificationBar()
Display a notification bar at the top or bottom of the viewport.
Parameters:
content(string | Element) - HTML string or DOM element to displayoptions(NotificationBarOptions) - Optional configuration
Returns: NotificationBarResult with element and close() function
// Show a simple notification at the top
const { close } = window.sesamy.content.showNotificationBar(`
<my-announcement-banner></my-announcement-banner>
`);
// Show at bottom
window.sesamy.content.showNotificationBar(bannerElement, {
position: 'bottom'
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
position | 'top' | 'bottom' | 'top' | Position of the bar |
fixed | boolean | true | Whether bar stays fixed on scroll |
zIndex | number | 2147483647 | CSS z-index |
autoDismissMs | number | - | Auto-close after milliseconds |
pushContent | boolean | false | Push page content to make room |
height | string | - | Bar height (required when pushContent is true) |
transitionDuration | string | '0.3s' | Animation duration |
// Push page content down with animation
window.sesamy.content.showNotificationBar(`
<div style="background:#28a745;color:white;padding:12px;text-align:center;">
🎉 Welcome! You have a new subscription.
</div>
`, {
pushContent: true,
height: '50px'
});
// Auto-dismiss after 5 seconds
window.sesamy.content.showNotificationBar(alertElement, {
autoDismissMs: 5000,
pushContent: true,
height: '40px'
});Use Cases
Custom signup form modal:
window.sesamy.content.showModal(`
<sesamy-signup-form
on-success="this.closest('[data-sesamy-modal]').remove()">
</sesamy-signup-form>
`);Cookie consent banner:
const { close } = window.sesamy.content.showNotificationBar(`
<div style="background:#333;color:white;padding:16px;display:flex;justify-content:center;gap:16px;align-items:center;">
<span>We use cookies to improve your experience.</span>
<button onclick="acceptCookies()">Accept</button>
</div>
`, {
position: 'bottom',
pushContent: true,
height: '60px'
});
function acceptCookies() {
localStorage.setItem('cookies-accepted', 'true');
close();
}Subscription success notification:
window.addEventListener('sesamyPurchaseComplete', () => {
window.sesamy.content.showNotificationBar(`
<div style="background:#28a745;color:white;padding:12px;text-align:center;">
✅ Thank you for subscribing! Your content is now unlocked.
</div>
`, {
pushContent: true,
height: '50px',
autoDismissMs: 5000
});
});