> For the complete documentation index, see [llms.txt](https://docs.mychips.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mychips.io/react-native/sdk-native/sdk-native-custom-layouts.md).

# SDK Native -  Custom Layouts

> Prerequisite: Complete the [Native SDK — Customizations](https://chatgpt.com/c/SDK-Native-React-Native-Customizations.md) guide first.

When the default campaign card does not fit your app, render your own React Native component. You control the card's layout and styles; `MCNativeAdView` handles campaign fetching, the scrollable list, impression tracking, and default clicks.

***

### How It Works

Pass a function to `renderCampaign`. The SDK calls it for each campaign and places the returned card inside its own pressable item.

| Prop                                            | What it does                                                                              |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `renderCampaign(campaign, index)`               | Returns your custom card for one `MCCampaign`.                                            |
| `horizontal`                                    | Chooses a horizontal carousel (`true`, the default) or vertical list (`false`).           |
| `renderSkeleton(index)`                         | Optionally returns a loading placeholder matching your card.                              |
| `onCampaignClick(campaign, index, openDefault)` | Optionally replaces click behavior; call `openDefault()` to continue with the SDK action. |

React Native uses components and styles, so there is no XML layout, `MCNativeAdRenderer`, or `getItemLayoutId()` to implement. `MCNativeAdView` loads automatically when mounted. You can also call `ref.current?.load()` to refresh it later.

The three examples below use one shared helper file for the reward, currency icon, and badges. Create this file once before trying any example:

```typescript
// CampaignCardParts.tsx
import React from 'react';
import {Image, Text, View} from 'react-native';
import {MCCampaignStatus} from 'mychips-react-sdk';
import type {MCCampaign} from 'mychips-react-sdk';

// Replace with the URL for your app's currency icon.
const CURRENCY_ICON_URL = 'https://your.cdn/currency.png';

export function rewardText(value: number): string {
  return Math.round(value).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

export function isInProgress(campaign: MCCampaign): boolean {
  const status = campaign.progress?.status;
  return !!status &&
    status !== MCCampaignStatus.COMPLETED &&
    status !== MCCampaignStatus.CLOSED;
}

export function CurrencyIcon({size = 16}: {size?: number}) {
  return (
    <Image
      source={{uri: CURRENCY_ICON_URL}}
      style={{width: size, height: size}}
      resizeMode="contain"
    />
  );
}

export function PromoBadge({ratio}: {ratio: number}) {
  if (ratio <= 1) return null;
  const multiplier = Number.isInteger(ratio) ? String(ratio) : ratio.toFixed(1);
  return (
    <Text style={{backgroundColor: '#E65C3A', color: '#FFFFFF',
      borderRadius: 12, overflow: 'hidden', paddingHorizontal: 10,
      paddingVertical: 4, fontSize: 12, fontWeight: '700'}}>
      {multiplier}x Rewards
    </Text>
  );
}

export function ProgressBadge({campaign}: {campaign: MCCampaign}) {
  if (!isInProgress(campaign)) return null;
  return (
    <Text style={{alignSelf: 'flex-start', backgroundColor: '#E8EDF3',
      color: '#424B5A', borderRadius: 5, overflow: 'hidden',
      paddingHorizontal: 8, paddingVertical: 3, fontSize: 12}}>
      In Progress
    </Text>
  );
}
```

You may replace CurrencyIcon with a local image such as `source={require('./assets/my_currency_icon.png')}`. These are app components, so styles and assets belong to your app. The examples use light colors; adjust them for your theme.

### Example A: Circular Thumbnails

This example creates a horizontal list with circular thumbnails, promo badges, in-progress indicators, and a currency icon.

<figure><img src="https://1889388802-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8FPXPNk0nLIRsJsg3nxP%2Fuploads%2FsjJGMjk1iap2eeA3ea8y%2Fimage.png?alt=media&amp;token=eadf7720-e3c7-4a36-962f-7f6a58e5c096" alt=""><figcaption></figcaption></figure>

#### Step A.1: Create the card component

```typescript
// CircularCampaignCard.tsx
import React from 'react';
import {Image, Text, View} from 'react-native';
import type {MCCampaign} from 'mychips-react-sdk';
import {CurrencyIcon, ProgressBadge, PromoBadge, rewardText}
  from './CampaignCardParts';

export function CircularCampaignCard({campaign}: {campaign: MCCampaign}) {
  const imageUrl = campaign.creatives?.thumbnail || campaign.creatives?.cover;

  return (
    <View style={{width: 140, minHeight: 222, marginRight: 24}}>
      <View style={{paddingTop: 12}}>
        {imageUrl ? (
          <Image source={{uri: imageUrl}} resizeMode="cover"
            style={{width: 140, height: 140, borderRadius: 70}} />
        ) : (
          <View style={{width: 140, height: 140, borderRadius: 70,
            backgroundColor: '#E8E8E8'}} />
        )}
        {campaign.promoRatio > 1 && (
          <View style={{position: 'absolute', top: 0, left: 0}}>
            <PromoBadge ratio={campaign.promoRatio} />
          </View>
        )}
      </View>
      <Text numberOfLines={2} style={{marginTop: 6, fontSize: 14,
        fontWeight: '600', color: '#212121'}}>
        {campaign.name}
      </Text>
      <View style={{flexDirection: 'row', alignItems: 'center', marginTop: 2}}>
        <CurrencyIcon size={14} />
        <Text style={{marginLeft: 3, fontSize: 16, fontWeight: '700',
          color: '#212121'}}>
          {rewardText(campaign.totalConvertedValue)}
        </Text>
      </View>
      <View style={{marginTop: 4}}>
        <ProgressBadge campaign={campaign} />
      </View>
    </View>
  );
}
```

#### Step A.2: Use the custom card

```typescript
import React from 'react';
import {MCNativeAdView} from 'mychips-react-sdk';
import {CircularCampaignCard} from './CircularCampaignCard';

export function CircularCampaigns() {
  return (
    <MCNativeAdView
      style={{height: 260}}
      horizontal
      renderCampaign={(campaign) => (
        <CircularCampaignCard campaign={campaign} />
      )}
    />
  );
}
```

### Example B: Vertical Layout (small cards)

This example renders compact vertical rows: a rounded thumbnail on the left, a title and progress badge in the center, and the reward on the right. A promo badge sits above the card's top-right corner.

<figure><img src="https://1889388802-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8FPXPNk0nLIRsJsg3nxP%2Fuploads%2F3xymQCFJJvq81ZgExvpZ%2Fimage.png?alt=media&amp;token=b4d63e3e-a7bf-4b06-8d14-eed9aaf4eaad" alt=""><figcaption></figcaption></figure>

#### Step B.1: Create the card component

```typescript
// SmallCampaignCard.tsx
import React from 'react';
import {Image, Text, View} from 'react-native';
import type {MCCampaign} from 'mychips-react-sdk';
import {CurrencyIcon, ProgressBadge, PromoBadge, rewardText}
  from './CampaignCardParts';

export function SmallCampaignCard({campaign}: {campaign: MCCampaign}) {
  const imageUrl = campaign.creatives?.thumbnail || campaign.creatives?.cover;

  return (
    <View style={{marginTop: 12, marginBottom: 8}}>
      <View style={{height: 92, borderRadius: 12, backgroundColor: '#F5F6F8',
        flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16}}>
        {imageUrl ? (
          <Image source={{uri: imageUrl}} resizeMode="cover"
            style={{width: 60, height: 60, borderRadius: 10}} />
        ) : (
          <View style={{width: 60, height: 60, borderRadius: 10,
            backgroundColor: '#E8E8E8'}} />
        )}
        <View style={{flex: 1, marginHorizontal: 12}}>
          <Text numberOfLines={1} style={{color: '#212121', fontSize: 14,
            fontWeight: '600'}}>
            {campaign.name}
          </Text>
          <View style={{marginTop: 4}}>
            <ProgressBadge campaign={campaign} />
          </View>
        </View>
        <View style={{flexDirection: 'row', alignItems: 'center'}}>
          <CurrencyIcon />
          <Text style={{marginLeft: 3, color: '#212121', fontSize: 16,
            fontWeight: '700'}}>
            {rewardText(campaign.totalConvertedValue)}
          </Text>
        </View>
      </View>
      {campaign.promoRatio > 1 && (
        <View style={{position: 'absolute', top: 0, right: 12}}>
          <PromoBadge ratio={campaign.promoRatio} />
        </View>
      )}
    </View>
  );
}
```

#### Step B.2: Use a vertical list

```typescript
import React from 'react';
import {MCNativeAdView} from 'mychips-react-sdk';
import {SmallCampaignCard} from './SmallCampaignCard';

export function SmallCampaigns() {
  return (
    <MCNativeAdView
      horizontal={false}
      style={{height: 450}}
      contentContainerStyle={{paddingHorizontal: 16}}
      renderCampaign={(campaign) => (
        <SmallCampaignCard campaign={campaign} />
      )}
    />
  );
}
```

### Example C: Vertical Layout (big cards)

This example makes a vertical list of large cards with a wide cover image, title, progress badge, and full-width green reward pill. It uses cover first and falls back to thumbnail only if no cover is available.

<figure><img src="https://1889388802-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8FPXPNk0nLIRsJsg3nxP%2Fuploads%2Fw3y9ikxTVamoJxAS0AcU%2Fimage.png?alt=media&amp;token=022a1530-8b69-465a-a954-15d4fd948ee8" alt=""><figcaption></figcaption></figure>

#### Step C.1: Create the card component

```typescript
// BigCampaignCard.tsx
import React from 'react';
import {Image, Text, View} from 'react-native';
import type {MCCampaign} from 'mychips-react-sdk';
import {CurrencyIcon, ProgressBadge, PromoBadge, rewardText}
  from './CampaignCardParts';

export function BigCampaignCard({campaign}: {campaign: MCCampaign}) {
  const imageUrl = campaign.creatives?.cover || campaign.creatives?.thumbnail;

  return (
    <View style={{width: '100%', maxWidth: 343, alignSelf: 'center',
      marginBottom: 16, borderRadius: 12, backgroundColor: '#F5F6F8',
      overflow: 'hidden'}}>
      <View style={{height: 172}}>
        {imageUrl ? (
          <Image source={{uri: imageUrl}} resizeMode="cover"
            style={{width: '100%', height: 172}} />
        ) : (
          <View style={{width: '100%', height: 172,
            backgroundColor: '#E8E8E8'}} />
        )}
        {campaign.promoRatio > 1 && (
          <View style={{position: 'absolute', bottom: 12, right: 12}}>
            <PromoBadge ratio={campaign.promoRatio} />
          </View>
        )}
      </View>
      <Text numberOfLines={1} style={{marginTop: 10, marginHorizontal: 12,
        color: '#212121', fontSize: 16, fontWeight: '700'}}>
        {campaign.name}
      </Text>
      <View style={{marginTop: 2, marginHorizontal: 12}}>
        <ProgressBadge campaign={campaign} />
      </View>
      <View style={{height: 47, marginTop: 10, marginHorizontal: 12,
        marginBottom: 12, borderRadius: 24, backgroundColor: '#2FA267',
        flexDirection: 'row', alignItems: 'center', justifyContent: 'center'}}>
        <CurrencyIcon size={20} />
        <Text style={{marginLeft: 4, color: '#FFFFFF', fontSize: 18,
          fontWeight: '700'}}>
          {rewardText(campaign.totalConvertedValue)}
        </Text>
      </View>
    </View>
  );
}
```

#### Step C.2: Use a vertical list

```typescript
import React from 'react';
import {MCNativeAdView} from 'mychips-react-sdk';
import {BigCampaignCard} from './BigCampaignCard';

export function BigCampaigns() {
  return (
    <MCNativeAdView
      horizontal={false}
      style={{height: 600}}
      contentContainerStyle={{paddingHorizontal: 16}}
      renderCampaign={(campaign) => (
        <BigCampaignCard campaign={campaign} />
      )}
    />
  );
}
```

### Key Points for Custom Layouts

#### Click Handling

`MCNativeAdView` wraps each custom card in a Pressable. Tapping it opens the campaign detail page by default. To add your own action and then continue with the normal SDK behavior:

```typescript
<MCNativeAdView
  style={{height: 260}}
  renderCampaign={(campaign) => <CircularCampaignCard campaign={campaign} />}
  onCampaignClick={(campaign, index, openDefault) => {
    // Optional: record your own UI event or show a confirmation.
    openDefault();
  }}
/>
```

If you provide `onCampaignClick`, call `openDefault()` when you want the SDK to perform its normal click tracking and navigation. Do not place a second Pressable around the whole custom card unless you intend to handle nested touches.

#### Image Loading

Use React Native's Image with `source={{uri: imageUrl}}` for campaign images and remote currency icons, or require(...) for a local icon. Check for an empty image URL and show a placeholder. The example uses `resizeMode="cover"`; the big card chooses creatives.cover before thumbnail.

#### List Recycling and Loading

The list can reuse rendered items, so derive every badge, reward, and image from the current campaign. The SDK tracks an impression when a campaign becomes visible; do not call `MCOfferwallSDK.trackImpression` again for cards in this view.

`MCNativeAdView` shows its built-in skeleton while loading. If the default shape does not fit your card, pass `renderSkeleton={(index) => <YourPlaceholder />}` and set `skeletonCount` as needed. Give the ad view an appropriate height, especially for a vertical list inside another screen.

### Next Steps

Need the campaign fields and status constants? See [Data Reference](https://chatgpt.com/c/SDK%20Native%20-%20Data%20Reference.md) →

Want small changes to the default card? See [Customizations](https://chatgpt.com/c/SDK-Native-React-Native-Customizations.md) →
