npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mr.dj2u/ads

v1.0.2

Published

Offline-first ads service and components for David J Grimsley's portfolio services

Downloads

256

Readme

@mr.dj2u/ads-sdk

Offline-first ads service and components for React Native/Expo apps. Display tasteful, non-intrusive ads for your services with built-in caching, analytics tracking, and customizable styling.

Needs External API You will need to set up an API like I have in DavidsPortfolio

Out-of-the-box ready: Works immediately with my portfolio services as a default so you can see it working right away but you will want to see the section below for initializing to your data.

Features

  • 🎨 Minimal Design - Blends seamlessly with your app's aesthetic
  • 📱 Cross-Platform - iOS, Android, and Web support
  • 💾 Offline-First - Caches ads locally with background refresh
  • 🎯 Smart Defaults - Works without configuration (uses davidjgrimsley.com)
  • 🔧 Fully Customizable - Override URLs and components per project
  • 📊 Analytics Ready - Built-in impression and click tracking
  • 🎭 Uniwind/NativeWind Support - Use Tailwind className for styling

Installation

npm install @davidjgrimsley/ads-sdk

Peer Dependencies

Ensure you have these installed:

npx expo install @react-native-async-storage/async-storage @expo/vector-icons expo-linking expo-linear-gradient

Quick Start

Basic Usage (No Configuration Required)

The SDK works out-of-the-box with default URLs pointing to davidjgrimsley.com:

import { AdBannerWithModal } from '@davidjgrimsley/ads-sdk';

export default function HomeScreen() {
  return (
    <View>
      <Text>Welcome to my app!</Text>
      <AdBannerWithModal className="mx-4 my-2" />
    </View>
  );
}

Optional Configuration

To use your own API and intake URLs, call initializeAdsSDK() once at app startup:

// app/_layout.tsx or app/index.tsx
import { initializeAdsSDK } from '@davidjgrimsley/ads-sdk';

// Using environment variables (recommended)
initializeAdsSDK({
  apiUrl: process.env.EXPO_PUBLIC_ADS_API_URL,
  intakeBaseUrl: process.env.EXPO_PUBLIC_ADS_INTAKE_URL,
  cacheDuration: 1000 * 60 * 60, // Optional: 1 hour (default)
});

// Or hardcode directly
initializeAdsSDK({
  apiUrl: 'https://yoursite.com/api/content',
  intakeBaseUrl: 'https://yoursite.com',
});

Environment Variables Example (.env):

EXPO_PUBLIC_ADS_API_URL=https://yoursite.com/api/content
EXPO_PUBLIC_ADS_INTAKE_URL=https://yoursite.com

Note: If you don't call initializeAdsSDK(), the SDK defaults to davidjgrimsley.com URLs. This gives you free exposure while keeping your app functional!

Usage in Feed/Scrollable Lists

Display ads every 15-20 items in your FlatList or ScrollView:

import { FlatList } from 'react-native';
import { AdBannerWithModal } from '@davidjgrimsley/ads-sdk';

export default function FeedScreen() {
  const renderItem = ({ item, index }: { item: Post; index: number }) => {
    // Show ad every 15 items
    if (index > 0 && index % 15 === 0) {
      return <AdBannerWithModal className="mx-4 my-2" />;
    }
    
    return <PostCard post={item} />;
  };

  return (
    <FlatList
      data={posts}
      renderItem={renderItem}
      keyExtractor={(item, index) => 
        item.id || `ad-${Math.floor(index / 15)}`
      }
    />
  );
}

For ScrollView with mixed content:

<ScrollView>
  {posts.map((post, index) => (
    <React.Fragment key={post.id}>
      <PostCard post={post} />
      {/* Ad every 20 posts */}
      {(index + 1) % 20 === 0 && (
        <AdBannerWithModal className="mx-4 my-4" />
      )}
    </React.Fragment>
  ))}
</ScrollView>

Components

AdBannerWithModal

Complete solution with banner, detail modal, and info modal.

<AdBannerWithModal 
  className="mx-4 my-2"
  adId="web-design" // Optional: show specific ad
/>

Props:

  • className?: string - NativeWind/Tailwind classes
  • adId?: string - Show specific ad instead of random rotation
  • BannerComponent?: React.ComponentType<AdBannerProps> - Custom banner component
  • ModalComponent?: React.ComponentType<AdModalProps> - Custom modal component

Individual Components

Use components separately for custom layouts:

import { AdBanner, AdModal, AdInfoModal } from '@davidjgrimsley/ads-sdk';

function CustomAdLayout() {
  const [ad, setAd] = useState<AdConfig | null>(null);
  const [modalVisible, setModalVisible] = useState(false);

  useEffect(() => {
    getRandomAd().then(setAd);
  }, []);

  return (
    <>
      <AdBanner 
        ad={ad} 
        onPress={() => setModalVisible(true)}
      />
      <AdModal 
        visible={modalVisible} 
        ad={ad}
        onClose={() => setModalVisible(false)} 
      />
    </>
  );
}

Service API

Access ads data directly for custom implementations:

import { 
  getAllAds, 
  getRandomAd, 
  getAdById, 
  refreshAds,
  clearAdsCache,
  trackAdEvent 
} from '@davidjgrimsley/ads-sdk';

// Fetch all cached ads
const ads = await getAllAds();

// Get random ad for rotation
const randomAd = await getRandomAd();

// Get specific ad
const ad = await getAdById('web-design');

// Force refresh from API
await refreshAds();

// Clear cache
await clearAdsCache();

// Track analytics
await trackAdEvent('impression', adId);
await trackAdEvent('click', adId);

Advanced Customization

Custom Banner Component

import { 
  AdBannerWithModal, 
  type AdBannerProps 
} from '@davidjgrimsley/ads-sdk';

function MyCustomBanner({ ad, onPress, onDismiss }: AdBannerProps) {
  return (
    <Pressable onPress={onPress} className="bg-purple-600 p-4 rounded-lg">
      <Text className="text-white font-bold">{ad.title}</Text>
      <Text className="text-purple-200">{ad.tagline}</Text>
      <TouchableOpacity onPress={onDismiss}>
        <Text className="text-white">✕</Text>
      </TouchableOpacity>
    </Pressable>
  );
}

// Use your custom banner
<AdBannerWithModal BannerComponent={MyCustomBanner} />

Custom Modal Component

import { type AdModalProps } from '@davidjgrimsley/ads-sdk';

function MyCustomModal({ visible, ad, onClose }: AdModalProps) {
  return (
    <Modal visible={visible} onRequestClose={onClose}>
      {/* Your custom modal design */}
    </Modal>
  );
}

<AdBannerWithModal ModalComponent={MyCustomModal} />

TypeScript

Full TypeScript support with exported types:

import type { 
  AdConfig, 
  AdsSDKConfig,
  AdBannerProps,
  AdModalProps,
  AdBannerWithModalProps 
} from '@davidjgrimsley/ads-sdk';

API Response Format

Your API endpoint should return this structure:

{
  "version": "1.0.0",
  "generatedAt": "2026-02-02T12:00:00Z",
  "services": [
    {
      "id": "web-design",
      "title": "Web Design",
      "tagline": "Beautiful, responsive websites",
      "description": "Full description...",
      "ctaText": "Get Started",
      "intakeUrl": "/services/web-design",
      "icon": "💻",
      "accentColor": "blue",
      "features": ["Feature 1", "Feature 2"]
    }
  ]
}

License

MIT © David J Grimsley