> 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-customizations.md).

# SDK Native - Customizations

> Prerequisite: Complete the [Native SDK - Quick Start](https://chatgpt.com/react-native/sdk-native.md) first.

These customizations require no custom card component — configure the built-in `MCNativeAdView` with props. The examples below use TypeScript and React Native components.

***

### 1. Configuration Options

#### 1.1 Scroll Direction

```typescript
// Horizontal (default)
<MCNativeAdView horizontal={true} style={{ height: 260 }} />

// Vertical list
<MCNativeAdView horizontal={false} style={{ height: 500 }} />
```

<figure><img src="https://1889388802-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8FPXPNk0nLIRsJsg3nxP%2Fuploads%2FZ1hlCFi0o9AQaqdPL6Uc%2Fimage.png?alt=media&amp;token=ee839c24-f5d6-4f5c-9164-ebcc15fd25b8" alt=""><figcaption></figcaption></figure>

Give a vertical list a bounded height or an appropriate flex layout.

#### 1.2 Maximum Number of Campaigns

```typescript
<MCNativeAdView maxCampaigns={5} style={{ height: 260 }} />
// Show all campaigns returned by the API (default):
<MCNativeAdView maxCampaigns={0} style={{ height: 260 }} />
```

`maxCampaigns` limits the cards displayed after fetching. It does not change the API request limit. To change how many campaigns the API requests, call `await MCOfferwallSDK.setLimit(10)` before mounting the view; the SDK's default request limit is 10.

#### 1.3 Nested Scrolling

Nested scrolling is enabled by default. If your screen does not need it, you can disable it:

```typescript
<MCNativeAdView nestedScrollEnabled={false} style={{ height: 260 }} />
```

#### 1.4 Open Campaign Details In-App

By default, tapping a campaign opens its detail page in the device's external browser. To keep users inside your app:

```typescript
await MCOfferwallSDK.setOpenInApp(true);
MCOfferwallSDK.setToolbarTitle('My Rewards'); // optional
```

Set this before the user taps a campaign. The SDK presents a React Native Modal containing react-native-webview. Install the WebView dependency as described in [SDK Installation](https://docs.mychips.io/react-native/install-sdk). The React Native implementation does not need an Android Activity declaration.

### 2. Change the Currency Icon

The default card shows a coin icon next to the reward value.&#x20;

<figure><img src="https://1889388802-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8FPXPNk0nLIRsJsg3nxP%2Fuploads%2FJw8jEnqJHEUo2RBwyyRC%2Fimage.png?alt=media&amp;token=2fb34d55-5f9d-42f7-affe-cff11ac1a19a" alt=""><figcaption></figcaption></figure>

You can replace it in two ways:

#### Option A: Set a custom URL

```typescript
<MCNativeAdView
  currencyIconUrl="https://example.com/my_coin.png"
  style={{ height: 260 }}
/>
```

#### Option B: Use a local image

```typescript
<MCNativeAdView
  currencyIconSource={require('./assets/my_currency_icon.png')}
  style={{ height: 260 }}
/>
```

Important: currencyIconSource takes precedence over the URL, so you do not need to disable the default URL when supplying a local image. The built-in card selects one source before it renders. To hide the icon, pass `currencyIconSource={null} or currencyIconUrl=""`.

These props affect the built-in card only. A card supplied through renderCampaign renders its own currency icon.

### 3. Customize Text Colors

Use defaultCardStyles to change the built-in card's colors after its default theme colors:

```typescript
<MCNativeAdView
  style={{ height: 260 }}
  defaultCardStyles={{
    title: { color: '#1976D2' },
    reward: { color: '#1976D2' },
    promo: { color: '#FFFFFF', backgroundColor: '#B23A00' },
    progress: { color: '#424B5A', backgroundColor: '#E8F2FF' },
  }}
/>
```

The default campaign name and reward text follow the app's light/dark appearance. If you set fixed colors, choose values readable in both themes. `defaultCardStyles` is ignored when `renderCampaign` is provided.

Default Card Style Keys

| Style key      | React Native style type | Description                         |
| -------------- | ----------------------- | ----------------------------------- |
| `card`         | `ViewStyle`             | Outer card container                |
| `image`        | `ImageStyle`            | Campaign image or empty placeholder |
| `title`        | `TextStyle`             | Campaign name                       |
| `reward`       | `TextStyle`             | Reward value                        |
| `currencyIcon` | `ImageStyle`            | Currency icon                       |
| `promo`        | `TextStyle`             | Promo badge                         |
| `progress`     | `TextStyle`             | In Progress badge                   |

The default card uses a thumbnail image if available and otherwise falls back to cover. Its image is 140 × 140 with rounded corners. To build a wider card with a cover image, see [Custom Layouts](https://chatgpt.com/react-native/sdk-native/sdk-native-custom-layout.md).

### 4. Custom Loading View

By default, the SDK shows a pulsing skeleton placeholder while loading. It uses five placeholders for a horizontal list and three for a vertical list.&#x20;

<figure><img src="https://1889388802-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8FPXPNk0nLIRsJsg3nxP%2Fuploads%2FVZdYYE4jyI4nisBWrzAO%2Fimage.png?alt=media&amp;token=d44b8782-f98e-4459-87e6-4d7b3b5b8fb7" alt=""><figcaption></figcaption></figure>

You can replace it:

```typescript
import { ActivityIndicator } from 'react-native';

<MCNativeAdView
  style={{ height: 260 }}
  loadingView={<ActivityIndicator size="large" />}
/>
```

Or use a spinner with text:

```typescript
import { ActivityIndicator, Text, View } from 'react-native';

<MCNativeAdView
  style={{ height: 260 }}
  loadingView={
    <View style={{ height: 260, alignItems: 'center', justifyContent: 'center' }}>
      <ActivityIndicator />
      <Text style={{ marginTop: 8 }}>Loading offers...</Text>
    </View>
  }
/>
```

Omit loadingView to restore the default skeleton. To keep its pulsing animation but change each placeholder's shape, use `renderSkeleton`:

```typescript
<MCNativeAdView
  style={{ height: 260 }}
  skeletonCount={4}
  renderSkeleton={() => (
    <View style={{ width: 140, height: 140, borderRadius: 70,
      margin: 8, backgroundColor: '#D0D0D0' }} />
  )}
/>
```

The built-in skeleton also copies the image dimensions and corner radii from `defaultCardStyles.image`.

<figure><img src="https://1889388802-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8FPXPNk0nLIRsJsg3nxP%2Fuploads%2FIwi9xr4DmmkLKq3oA6vm%2Fimage.png?alt=media&amp;token=3e357c3f-8bd9-4fb3-b668-f058fb5729ea" alt=""><figcaption></figcaption></figure>

### 5. Custom Click Handler

Override the default click behavior for a view:

```typescript
<MCNativeAdView
  style={{ height: 260 }}
  onCampaignClick={(campaign, position, openDefault) => {
    console.log('Selected', campaign.id, position);
    // Show your own UI, then continue with the SDK's default detail opening:
    openDefault();
  }}
/>
```

When `onCampaignClick` is set, call `openDefault()` if you want the SDK's default action. If you omit it, your callback handles navigation. Automatic impression tracking remains active.

> Tip: You can change the default detail behavior globally without writing a custom click handler. Call await `MCOfferwallSDK.setOpenInApp(true)` to show details in the app's WebView modal instead of the external browser.

### 6. Loading Lifecycle Listener

Monitor loading state for your own UI logic:

```typescript
<MCNativeAdView
  style={{ height: 260 }}
  onLoadingStarted={() => {
    console.log('Loading campaigns');
  }}
  onCampaignsLoaded={(campaigns, meta) => {
    console.log('Displayed:', campaigns.length, 'API count:', meta.count);
  }}
  onError={(error) => {
    console.error('Campaign load failed:', error);
  }}
/>
```

`onCampaignsLoaded` receives the displayed campaigns after `maxCampaigns` is applied, plus the API metadata. It is also called when the result is `empty.onError` receives an Error object.

### Next Steps

Need a completely different card design? [See Custom Layouts →](/react-native/sdk-native/sdk-native-custom-layouts.md)

Need the full data reference? See [Data Reference →](/react-native/sdk-native/sdk-native-data-reference.md)
