> ## Documentation Index
> Fetch the complete documentation index at: https://www.cometchat.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Control Panel

> CometChat Calling SDK v5 - Custom Control Panel for React Native

Build a custom control panel to replace or extend the default call controls. Hide the default layout and implement your own UI using the SDK's action methods.

## Hide Default Layout

Disable the default control panel to use your own:

```tsx theme={null}
import { CometChatCalls } from '@cometchat/calls-sdk-react-native';

const callSettings = new CometChatCalls.CallSettingsBuilder()
  .enableDefaultLayout(false)
  .build();
```

## Available Actions

Use these methods to control the call from your custom UI:

| Action             | Method                                           | Description              |
| ------------------ | ------------------------------------------------ | ------------------------ |
| Mute Audio         | `CometChatCalls.muteAudio()`                     | Mute local microphone    |
| Unmute Audio       | `CometChatCalls.unMuteAudio()`                   | Unmute local microphone  |
| Pause Video        | `CometChatCalls.pauseVideo()`                    | Stop sending video       |
| Resume Video       | `CometChatCalls.resumeVideo()`                   | Resume sending video     |
| Switch Camera      | `CometChatCalls.switchCamera()`                  | Toggle front/rear camera |
| Leave Session      | `CometChatCalls.leaveSession()`                  | End the call             |
| Raise Hand         | `CometChatCalls.raiseHand()`                     | Raise hand               |
| Lower Hand         | `CometChatCalls.lowerHand()`                     | Lower hand               |
| Set Layout         | `CometChatCalls.setLayout(layout)`               | Change call layout       |
| Start Recording    | `CometChatCalls.startRecording()`                | Start recording          |
| Stop Recording     | `CometChatCalls.stopRecording()`                 | Stop recording           |
| Start Screen Share | `CometChatCalls.startScreenSharing()`            | Share screen             |
| Stop Screen Share  | `CometChatCalls.stopScreenSharing()`             | Stop sharing             |
| Enable PiP         | `CometChatCalls.enablePictureInPictureLayout()`  | Enter PiP mode           |
| Disable PiP        | `CometChatCalls.disablePictureInPictureLayout()` | Exit PiP mode            |

## Complete Example

```tsx theme={null}
import React, { useState, useEffect } from 'react';
import { View, TouchableOpacity, Text, StyleSheet, SafeAreaView } from 'react-native';
import { CometChatCalls } from '@cometchat/calls-sdk-react-native';

interface CustomControlPanelProps {
  isAudioOnly?: boolean;
}

function CustomControlPanel({ isAudioOnly = false }: CustomControlPanelProps) {
  const [isAudioMuted, setIsAudioMuted] = useState(false);
  const [isVideoMuted, setIsVideoMuted] = useState(false);
  const [isHandRaised, setIsHandRaised] = useState(false);
  const [isRecording, setIsRecording] = useState(false);
  const [currentLayout, setCurrentLayout] = useState<'TILE' | 'SIDEBAR' | 'SPOTLIGHT'>('TILE');

  useEffect(() => {
    // Listen for media events
    const unsubscribeAudioMuted = CometChatCalls.addEventListener(
      'onAudioMuted',
      () => setIsAudioMuted(true)
    );
    const unsubscribeAudioUnmuted = CometChatCalls.addEventListener(
      'onAudioUnMuted',
      () => setIsAudioMuted(false)
    );
    const unsubscribeVideoPaused = CometChatCalls.addEventListener(
      'onVideoPaused',
      () => setIsVideoMuted(true)
    );
    const unsubscribeVideoResumed = CometChatCalls.addEventListener(
      'onVideoResumed',
      () => setIsVideoMuted(false)
    );
    const unsubscribeRecordingStarted = CometChatCalls.addEventListener(
      'onRecordingStarted',
      () => setIsRecording(true)
    );
    const unsubscribeRecordingStopped = CometChatCalls.addEventListener(
      'onRecordingStopped',
      () => setIsRecording(false)
    );
    const unsubscribeLayoutChanged = CometChatCalls.addEventListener(
      'onCallLayoutChanged',
      (layout: 'TILE' | 'SIDEBAR' | 'SPOTLIGHT') => setCurrentLayout(layout)
    );

    return () => {
      unsubscribeAudioMuted();
      unsubscribeAudioUnmuted();
      unsubscribeVideoPaused();
      unsubscribeVideoResumed();
      unsubscribeRecordingStarted();
      unsubscribeRecordingStopped();
      unsubscribeLayoutChanged();
    };
  }, []);

  const toggleAudio = () => {
    if (isAudioMuted) {
      CometChatCalls.unMuteAudio();
    } else {
      CometChatCalls.muteAudio();
    }
  };

  const toggleVideo = () => {
    if (isVideoMuted) {
      CometChatCalls.resumeVideo();
    } else {
      CometChatCalls.pauseVideo();
    }
  };

  const toggleHand = () => {
    if (isHandRaised) {
      CometChatCalls.lowerHand();
      setIsHandRaised(false);
    } else {
      CometChatCalls.raiseHand();
      setIsHandRaised(true);
    }
  };

  const toggleRecording = () => {
    if (isRecording) {
      CometChatCalls.stopRecording();
    } else {
      CometChatCalls.startRecording();
    }
  };

  const cycleLayout = () => {
    const layouts: Array<'TILE' | 'SIDEBAR' | 'SPOTLIGHT'> = ['TILE', 'SIDEBAR', 'SPOTLIGHT'];
    const currentIndex = layouts.indexOf(currentLayout);
    const nextLayout = layouts[(currentIndex + 1) % layouts.length];
    CometChatCalls.setLayout(nextLayout);
  };

  const handleEndCall = () => {
    CometChatCalls.leaveSession();
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.controlRow}>
        {/* Audio Toggle */}
        <TouchableOpacity
          style={[styles.controlButton, isAudioMuted && styles.activeButton]}
          onPress={toggleAudio}
        >
          <Text style={styles.buttonIcon}>{isAudioMuted ? '🔇' : '🎤'}</Text>
          <Text style={styles.buttonLabel}>
            {isAudioMuted ? 'Unmute' : 'Mute'}
          </Text>
        </TouchableOpacity>

        {/* Video Toggle (only for video calls) */}
        {!isAudioOnly && (
          <TouchableOpacity
            style={[styles.controlButton, isVideoMuted && styles.activeButton]}
            onPress={toggleVideo}
          >
            <Text style={styles.buttonIcon}>{isVideoMuted ? '📷' : '🎥'}</Text>
            <Text style={styles.buttonLabel}>
              {isVideoMuted ? 'Show' : 'Hide'}
            </Text>
          </TouchableOpacity>
        )}

        {/* Switch Camera (only for video calls) */}
        {!isAudioOnly && (
          <TouchableOpacity
            style={styles.controlButton}
            onPress={() => CometChatCalls.switchCamera()}
          >
            <Text style={styles.buttonIcon}>🔄</Text>
            <Text style={styles.buttonLabel}>Flip</Text>
          </TouchableOpacity>
        )}

        {/* Raise Hand */}
        <TouchableOpacity
          style={[styles.controlButton, isHandRaised && styles.handRaisedButton]}
          onPress={toggleHand}
        >
          <Text style={styles.buttonIcon}>{isHandRaised ? '✋' : '🤚'}</Text>
          <Text style={styles.buttonLabel}>
            {isHandRaised ? 'Lower' : 'Raise'}
          </Text>
        </TouchableOpacity>
      </View>

      <View style={styles.controlRow}>
        {/* Layout Toggle */}
        <TouchableOpacity style={styles.controlButton} onPress={cycleLayout}>
          <Text style={styles.buttonIcon}>📐</Text>
          <Text style={styles.buttonLabel}>{currentLayout}</Text>
        </TouchableOpacity>

        {/* Recording */}
        <TouchableOpacity
          style={[styles.controlButton, isRecording && styles.recordingButton]}
          onPress={toggleRecording}
        >
          <Text style={styles.buttonIcon}>{isRecording ? '⏹️' : '⏺️'}</Text>
          <Text style={styles.buttonLabel}>
            {isRecording ? 'Stop' : 'Record'}
          </Text>
        </TouchableOpacity>

        {/* End Call */}
        <TouchableOpacity
          style={[styles.controlButton, styles.endCallButton]}
          onPress={handleEndCall}
        >
          <Text style={styles.buttonIcon}>📞</Text>
          <Text style={styles.buttonLabel}>End</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    backgroundColor: 'rgba(0, 0, 0, 0.8)',
    paddingVertical: 16,
    paddingHorizontal: 8,
  },
  controlRow: {
    flexDirection: 'row',
    justifyContent: 'center',
    marginBottom: 12,
    gap: 12,
  },
  controlButton: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#333',
    width: 64,
    height: 64,
    borderRadius: 32,
  },
  activeButton: {
    backgroundColor: '#6851D6',
  },
  handRaisedButton: {
    backgroundColor: '#f59e0b',
  },
  recordingButton: {
    backgroundColor: '#ef4444',
  },
  endCallButton: {
    backgroundColor: '#dc2626',
  },
  buttonIcon: {
    fontSize: 24,
  },
  buttonLabel: {
    color: '#fff',
    fontSize: 10,
    marginTop: 4,
  },
});

export default CustomControlPanel;
```

## Using with Call Component

Combine the custom control panel with the call component:

```tsx theme={null}
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { CometChatCalls } from '@cometchat/calls-sdk-react-native';
import CustomControlPanel from './CustomControlPanel';

interface CallScreenProps {
  callToken: string;
  isAudioOnly?: boolean;
}

function CallScreen({ callToken, isAudioOnly = false }: CallScreenProps) {
  const callSettings = new CometChatCalls.CallSettingsBuilder()
    .enableDefaultLayout(false) // Hide default controls
    .setIsAudioOnlyCall(isAudioOnly)
    .build();

  return (
    <View style={styles.container}>
      <CometChatCalls.Component
        callToken={callToken}
        callSettings={callSettings}
      />
      <CustomControlPanel isAudioOnly={isAudioOnly} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#000',
  },
});

export default CallScreen;
```

## Related Documentation

* [Actions](/calls/react-native/actions) - All available call actions
* [Events](/calls/react-native/events) - Listen for call events
* [Session Settings](/calls/react-native/session-settings) - Configure call settings
