@almlah/ads-sdk
v0.1.0
Published
Headless advertisement SDK for Almlah Ads — usePlacement + automatic impression/click tracking. Framework-agnostic core with React bindings.
Maintainers
Readme
@almlah/ads-sdk — headless advertisement SDK
The only integration point for websites. It fetches ads, caches them, retries, and tracks impressions/clicks automatically. It is headless: it gives you data; you render it. It never controls layout.
Setup
Wrap your app once:
import { AdsProvider } from "@almlah/ads-sdk";
const adsConfig = { endpoint: "https://ads.example.com", platform: "website-a" };
export default function App({ children }) {
return <AdsProvider config={adsConfig}>{children}</AdsProvider>;
}Render a placement
usePlacement returns data + a getTrackProps prop-getter. Spread it onto each
ad element — impressions fire once when visible (IntersectionObserver), clicks on
click. No manual tracking calls.
import { usePlacement } from "@almlah/ads-sdk";
function HomepageProducts() {
const products = usePlacement("homepage-products");
if (products.isLoading) return <Skeleton />;
return (
<div className="grid">
{products.items.map((ad) => {
const c = ad.creative; // discriminated union on c.type
if (c.type !== "product") return null;
return (
<a key={ad.id} href={c.destinationUrl} {...products.getTrackProps(ad)}>
<img src={c.image.url} alt={c.productName} />
<strong>{c.brand}</strong> {c.productName} — {c.price}
<button>{c.ctaLabel}</button>
</a>
);
})}
</div>
);
}items is always an array, regardless of the placement's capacity.
Batching
Every usePlacement() that mounts in the same tick is coalesced into one
POST /api/v1/delivery request carrying all the requested placement keys — never
one request per placement.
API
| Export | What it does |
|---|---|
| <AdsProvider config> | Creates the client, wires unload-flush of tracking |
| usePlacement(key) | { items, status, isLoading, capacity, error, refresh, getTrackProps } |
| useTrack() | (token, "impression" \| "click") => void — low-level, rarely needed |
| useAds() | The AdsClient (advanced: prefetch/refresh/clearCache/track) |
| prefetchPlacement(...keys) | Warm the cache before render |
| refreshPlacement(key) | Invalidate + refetch |
| clearCache() | Drop all cached placements |
| AdsClient | Framework-agnostic core (use directly outside React) |
| createLocalStorageStore() | Optional offline cache backend for config.store |
Config
endpoint, platform (required) · context { country, language, device } ·
cacheTtlMs (60000) · maxRetries (2) · retryBackoffMs (300) ·
trackBatchSize (10) · trackFlushMs (2000) · store (offline cache) ·
fetchImpl, debug.
Behavior notes
- Caching with TTL; stale-while-error — a failed refetch keeps showing the last good ads instead of erroring.
- Retries with exponential backoff.
- Tracking is batched and flushed on a timer, on
trackBatchSize, and vianavigator.sendBeaconon page hide/visibility change. Impressions dedupe per token so an ad counts once. - SSR-safe (no fetch on the server; stable server snapshot).
Verify the core: npm run verify -w @almlah/ads-sdk.
