Skip to content

Server-Side Rendering 1.12.3 ​

The SDK provides a fetchAds() function for server-side rendering (SSR) workflows. This allows you to fetch ad data on the server and hydrate it on the client, enabling faster initial page loads and preventing layout shift.

WARNING

Impression Tracking: The server export only fetches ad data - it does not handle impression tracking. If you render ads without using the client SDK, you must implement tracking manually. See Impression Tracking for details.

Installation ​

The server functionality is included in the main SDK package:

bash
npm install @adhese/sdk
bash
pnpm add @adhese/sdk
bash
yarn add @adhese/sdk
bash
bun add @adhese/sdk

Usage ​

Import fetchAds from @adhese/sdk/server:

typescript
import { fetchAds } from '@adhese/sdk/server';

const ads = await fetchAds({
  account: 'your-account',
  location: 'homepage',
  slots: [
    { format: 'leaderboard' },
    { format: 'sidebar', slot: '1' },
  ],
});

POST vs GET Requests ​

The fetchAds function supports both POST and GET request methods:

MethodGlobal ParametersSlot ParametersUse Case
POST (default)SupportedSupportedMost use cases, supports all features
GETNot supportedNot supportedSimple requests, cacheable by CDN

Options ​

Main Options ​

OptionTypeRequiredDescription
accountstringYesYour Adhese account name
locationstringYesThe page/location identifier
slotsFetchAdsSlot[]YesArray of slot configurations
timeoutnumberNoRequest timeout in milliseconds
parametersRecord<string, string | string[]>NoGlobal parameters (POST only)
requestType'POST' | 'GET'NoRequest method (default: 'POST')

Slot Options ​

OptionTypeRequiredDescription
formatstringYesThe ad format identifier
slotstringNoSlot identifier for multiple slots of the same format
parametersRecord<string, string | string[]>NoSlot-specific parameters

Examples ​

Basic Usage ​

typescript
import { fetchAds } from '@adhese/sdk/server';

const ads = await fetchAds({
  account: 'demo',
  location: 'homepage',
  slots: [{ format: 'leaderboard' }],
});

console.log(ads[0]?.adType, ads[0]?.width, ads[0]?.height);

POST with Global Parameters ​

typescript
import { fetchAds } from '@adhese/sdk/server';

const ads = await fetchAds({
  account: 'demo',
  location: 'homepage',
  requestType: 'POST',
  parameters: {
    dt: 'desktop',
    tl: 'all',
  },
  slots: [
    {
      format: 'leaderboard',
      parameters: {
        position: 'top',
      },
    },
  ],
});

GET Request ​

typescript
import { fetchAds } from '@adhese/sdk/server';

const ads = await fetchAds({
  account: 'demo',
  location: 'homepage',
  requestType: 'GET',
  slots: [{ format: 'leaderboard' }],
});

Timeout Handling ​

Use timeout to prevent blocking page load:

typescript
import { fetchAds } from '@adhese/sdk/server';

const ads = await fetchAds({
  account: 'demo',
  location: 'homepage',
  slots: [{ format: 'leaderboard' }],
  timeout: 2000,
});

if (ads.length === 0) {
  // Render fallback content
}

Hydration Patterns ​

Server Fetch + Client Render ​

A common pattern is to fetch ads on the server and render them on the client:

tsx
// app/page.tsx (Next.js App Router Server Component)
import { fetchAds } from '@adhese/sdk/server';

export default async function HomePage() {
  const ads = await fetchAds({
    account: 'demo',
    location: 'homepage',
    slots: [{ format: 'leaderboard' }],
    timeout: 2000,
  });

  const ad = ads[0];

  return (
    <div>
      {ad && (
        <div
          style={{ width: ad.width, height: ad.height }}
          dangerouslySetInnerHTML={{ __html: ad.tag as string }}
        />
      )}
    </div>
  );
}

Preventing Layout Shift ​

Use the width and height from the ad response to reserve space:

jsx
function AdSlot({ ad }) {
  // Reserve space even before ad loads
  const style = {
    width: ad?.width ?? 728,  // fallback to expected dimensions
    height: ad?.height ?? 90,
    minHeight: ad?.height ?? 90,
  };

  return (
    <div style={style}>
      {ad && <div dangerouslySetInnerHTML={{ __html: ad.tag }} />}
    </div>
  );
}

Error Handling ​

Timeout Behavior ​

When a timeout is specified and exceeded, fetchAds returns an empty array instead of throwing:

typescript
const ads = await fetchAds({
  account: 'demo',
  location: 'homepage',
  slots: [{ format: 'leaderboard' }],
  timeout: 2000,
});

This enables graceful degradation without blocking page rendering.

Network Errors ​

Network errors and non-2xx responses will throw an error:

typescript
try {
  const ads = await fetchAds({
    account: 'demo',
    location: 'homepage',
    slots: [{ format: 'leaderboard' }],
  });
} catch (error) {
  console.error('Failed to fetch ads:', error);
}

Impression Tracking ​

When using the client SDK, impression tracking is handled automatically. However, when rendering ads server-side or in a custom implementation, you need to track impressions manually.

Each ad in the response includes tracking URLs that should be called at the appropriate time:

PropertyWhen to Call
impressionCounterWhen the ad is rendered/displayed
viewableImpressionCounterWhen the ad becomes viewable (typically 50% visible for 1 second)
trackerGeneral tracking pixel, call on render

Next.js Example ​

tsx
'use client';

import { addTrackingPixel } from '@adhese/sdk-shared';
import { useEffect, useRef } from 'react';
import type { AdheseAd } from '@adhese/sdk';

function AdSlot({ ad }: { ad: AdheseAd }) {
  const ref = useRef<HTMLDivElement>(null);
  const tracked = useRef(false);

  useEffect(() => {
    if (!ref.current || tracked.current) return;

    if (ad.impressionCounter) addTrackingPixel(ad.impressionCounter);
    if (ad.tracker) addTrackingPixel(ad.tracker);

    // IAB viewability standard: 50% visible for 1 second
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting && ad.viewableImpressionCounter) {
          setTimeout(() => {
            addTrackingPixel(ad.viewableImpressionCounter!);
            observer.disconnect();
          }, 1000);
        }
      },
      { threshold: 0.5 }
    );

    observer.observe(ref.current);
    tracked.current = true;

    return () => observer.disconnect();
  }, [ad]);

  return (
    <div
      ref={ref}
      style={{ width: ad.width, height: ad.height }}
      dangerouslySetInnerHTML={{ __html: ad.tag as string }}
    />
  );
}

For more information about slot lifecycle events when using the client SDK, see the Events documentation.

Notes ​

NOTE

Node.js Version: Requires Node.js 18+ for native fetch support.