Prompt boxes are an inherent part of the web, and their utility varies with their use. They are used to display messages, warnings, alerts, and confirm consent.

Through a reminder dialog, the user is usually given the option to agree, disagree, and cancel buttons. Sometimes alerts are also used to record user input, but this depends on the platform.

In this article, we’ll learn how to create a custom alert dialog in React Native to suit the needs of your project.

Introduction to the

React Native provides an Alert API that can be used to display local Alert dialogs on Android and iOS. But the local alert dialog has some limitations.

For example, on Android we can’t display more than three buttons, and there’s no option to capture user input. While iOS allows us to have lots of buttons and let users enter data, we still can’t show pictures, charts, or any form of customization other than text.

To handle these limitations, we need to create custom alert dialog boxes. A custom alert dialog can act like a mode and can support components.

Properties of the alert box

Before customizing any native components, we should have a clear understanding of their architecture and event handling.

For example, a button has multiple properties, such as a label and inflation. A button also has events such as press, hold, release, hover, etc. When we customize it, we need to take all these attributes into account. Otherwise, we lose the look, feel and function.

A local alert box has the following properties.

  1. Title – A text title to indicate the purpose of the alert. Android and iOS support.
  2. Message – a text message that explains a notification or warning. Android and iOS support
  3. Buttons – Android supports up to three buttons, while iOS supports unlimited buttons.
  4. External Knock – Android alerts can be turned off by tapping on the outside of the alarm.
  5. onPress– The ability to make a phone call at the touch of a button. Android and iOS support this feature
  6. onDismiss– The ability to call when the alarm is off. Only Android supports this feature
  7. Prompt – allows the user to enter data in the input field. Only iOS supports this feature
  8. Back Button – By default in Android, alerts are turned off when the back button is pressed.

We need to take all of these attributes into account when customizing the alert box.

The user interface and architecture of the alert dialog

Let’s take a look at what the local Alert looks like and where the different elements are on it. Android and iOS Alert look and feel different.

Android specifications

Following the Material Design concept, the layout and color of the Android alert dialog is as follows.

The element category attribute The value of
The container Maximum width

Border radius

The background color
280

2

#fafafa
Title text On the surface color

The opacity

Font weight

The size of the

case
# 000000

100%

The bold

22

Sentence case
Supporting text On the surface color

The opacity

Font weight

The size of the

case
# 000000

100%

Regular

15

Sentence Case
Button text primary color

The opacity

Font weight

The size of the

case
#387ef5

100%

500

16

The capital letters
scratches On the surface color

The opacity
#232F34

32%

Other attributes, such as height, width, padding, margins, and so on, are shown below.

Used for small button actions.

Source: material. IO

For long button actions.

Source: material. IO

Other attributes include elevation – 24dp position – center from both sides of the minimum margin – 48dp

The specifications of the iOS

Also, for iOS, we have the following specs.

The element category attribute The value of
The container The surface of the Border radius

Maximum width

The background color

z-index
13px

270px

#f8f8f8

10
Title text On the surface color

Padding

Margin Top

Align

Font

Size

Case
# 000000

12px 16px 7px

8px

Center

600

17px

Sentence Case
Supporting text On the surface color

Align

Padding

Font

Size

Case
# 000000

center

0px 16px 21px

conventional

13

Sentence case
Button in the container Common properties

The OK button

Single row and multiple buttons

– ordinary

– Non-OK button
The right margin

The minimum height

The top margin

The font size

color

Line height

Font weight

Minimum width

Right margin
0.55 px.

44px

0.55 px solid # DBDBDF

17px

#387ef5

20px

700

50%

0.55 px solid # DBDBDF
shear On the surface color

The opacity
# 000000

30%

This information is collected from the Ionic AlertController documentation.

Custom alert libraries and packages

There are libraries on GitHub that let you create custom alert boxes. Some of these are react-native awesome-alerts, react-native Dialog, and react-native Modal. You can try these libraries out, but in this article, we’ll customize the alert boxes without them.

The difference between alarms and modes

Alert and Modal are semantically the same thing, but differ in complexity and usability.

Reminder dialogs are designed to display short information in the simplest way possible, which is why they are limited in their functionality.

Modes, on the other hand, are used for complex displays. They require us to define the whole thing ourselves. By default, they provide event listeners, such as back button handlers.

Customize the alert dialog

Let’s take a look at what we’re going to customize. We should remember that alerts are used to display important information. This could be an error, warning, or notification. They are not for displaying pictures or filling out forms. For this, you should use templates.

In Alert, we will customize.

  1. Dialog box background color
  2. Font color, size, weight, etc. for headings and messages.
  3. The font color, background color, and border style of the button

Because React Native calls the Android and iOS Native Alert components, it doesn’t provide a direct way to customize them. Alerts are fixed components that have a clear purpose and are therefore not customizable in Android and iOS. Android developers use the Dialog class to do this.

In our case, we will use the React Native Modal API. The benefits of using this API are as follows.

  1. We don’t need to worry about the location of our custom alerts. It will stay at the top of the entire application
  2. Android’s back button and Apple TV’s menu button will be handled automatically.

React Native Modal

The React Native Modal API provides a container that is displayed in its surrounding View. A Boolean item visible is passed to the Modal component to show or hide it. There are other items, but we don’t care about them because they don’t work with Alert.

So that’s how Modal works.

import React, { useState } from 'react'; import { Modal, Text, Pressable, View } from 'react-native'; const App = () => { const [modalVisible, setModalVisible] = useState(false); return ( <View> <Modal animationType="fade" transparent={true} visible={modalVisible} onRequestClose={() => { setModalVisible(! modalVisible); }} > <View> /** * Anything here will display on Modal. * We need to create background overlay and Alert box. */ </View> </Modal> <View> /** * Main App content. This will get hidden when modal opens up */ </View> </View> ); }; export default App;Copy the code

ModalVisible is the state variable used to show or hide Modal. You can keep the modes on one page, or wrap the entire application around its parent View. It’s best to use a storage and state management library like Redux, as it can help you change modalVisible variables anywhere in your application.

Create a button to open itAlert

First, we will need an event to change the value of modalVisible. In our code, we will use a button. When this button is pressed, modalVisible becomes true and displays a Modal. To create this button, we will use the Pressable component.

import React, { useState } from 'react'; import { Modal, Text, Pressable, View } from 'react-native'; const App = () => { const [modalVisible, setModalVisible] = useState(false); return ( <View> <Modal animationType="fade" transparent={true} visible={modalVisible} onRequestClose={() => { setModalVisible(! modalVisible); }} > <View> /** * Anything here will display on Modal. * We need to create background overlay and Alert box. */ </View> </Modal> <View> <Pressable style={[styles.button, styles.buttonOpen]} onPress={() => setModalVisible(true)} > <Text style={styles.textStyle}>Show Modal</Text> </Pressable> </View> </View> ); }; const styles = StyleSheet.create({ centeredView: { flex: 1, justifyContent: "center", alignItems: "center", marginTop: 22 }, button: { borderRadius: 20, padding: 10, elevation: 2 }, buttonOpen: { backgroundColor: "#F194FF", }, textStyle: { color: "white", fontWeight: "bold", textAlign: "center" }, }); export default App;Copy the code

In this code, we added some styling to improve the look and feel of the buttons and other parts of the application. Currently, it will display a button. Clicking this button will open the mode. The rendered output will look like this.

Create a user interface for the alert dialog box

The next step is to create a modal UI. But first, we need to define a way to distinguish between the default UIs of the two operating systems. IOS has a different view than Android. React Native provides platform apis to identify operating systems.

Let’s start by creating the background. As we discussed in the UI and Architecture sections, the background color and opacity on Android are #232F34 and 0.32, respectively. For iOS, these values are #000000 and 0.3, respectively.

import React, { useState } from 'react'; import { Modal, Text, Pressable, View, Platform } from 'react-native'; const App = () => { const [modalVisible, setModalVisible] = useState(false); return ( <View> <Modal animationType="fade" transparent={true} visible={modalVisible} onRequestClose={() => { setModalVisible(! modalVisible); }} > <Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => setModalVisible(false)} /> <View> /** * Anything here will display on Modal. * We need to create background overlay and Alert box. */ </View> </Modal> <View> <Pressable style={[styles.button, styles.buttonOpen]} onPress={() => setModalVisible(true)} > <Text style={styles.textStyle}>Show Modal</Text> </Pressable> </View> </View> ); }; const styles = StyleSheet.create({ centeredView: { flex: 1, justifyContent: "center", alignItems: "center", marginTop: 22 }, button: { borderRadius: 20, padding: 10, elevation: 2 }, buttonOpen: { backgroundColor: "#F194FF", }, textStyle: { color: "white", fontWeight: "bold", textAlign: "center" }, iOSBackdrop: { backgroundColor: "#000000", opacity: Opacity opacity backdrop backdrop backdrop backdrop backdrop backdrop: {position: 'absolute', top: 0, left: 0, opacity backdrop backdrop: {position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, } }); export default App;Copy the code

The rendered output will look something like this.

Note that we used the Pressable component to create the background. This is because we wanted to add the ability to turn off the modes when the background wall is pressed.

Next we will create an alert dialog box on the background panel. But first, we should put Modal in a separate component. This will help to call our custom Alert in a different style.

import React, { useState } from "react"; import { Alert, Modal, StyleSheet, Text, Pressable, View, Platform } from "react-native"; const CustomAlert = (props) => { return ( <Modal animationType="fade" transparent={true} visible={props.modalVisible} onRequestClose={() => { props.setModalVisible(false); }} > <Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => props.setModalVisible(false)} /> <View> </View> </Modal> ) } const App = () => { const [modalVisible, setModalVisible] = useState(false); return ( <View style={styles.centeredView}> <CustomAlert modalVisible={modalVisible} setModalVisible={setModalVisible} /> <View> <Pressable style={[styles.button, styles.buttonOpen]} onPress={() => setModalVisible(true)} > <Text style={styles.textStyle}>Show Modal</Text> </Pressable> </View> </View> ); }; const styles = StyleSheet.create({ centeredView: { flex: 1, justifyContent: "center", alignItems: "center", marginTop: 22 }, button: { borderRadius: 20, padding: 10, elevation: 2 }, buttonOpen: { backgroundColor: "#F194FF", }, textStyle: { color: "white", fontWeight: "bold", textAlign: "center" }, iOSBackdrop: { backgroundColor: "#000000", opacity: Opacity opacity backdrop backdrop backdrop backdrop backdrop backdrop: {position: 'absolute', top: 0, left: 0, opacity backdrop backdrop: {position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, } }); export default App;Copy the code

We’ve created a different component called CustomAlert and put our Modal in it.

ModalVisible is in app.js state, but keep in mind that a better approach is to use a central storage management library such as Redux. Redux will make modalVisible state accessible to all components throughout the application without the need to pass modalVisible as a prop.

Now it’s time to set the default value for Modal. These values will design a box similar to the local alert box on Android and iOS.

We have defined all values in the UI and schema sections. Let’s start with Android.

Android custom alert dialog

The default value is.

Style properties value
The box

The background color

Maximum width

margin

highly

Border radius
transparent

280

48

24

2
The title

margin

color

The font size

Font weight
24

# 000000

22

bold
information

Margin on the left

Margin on the right

Margin on the bottom

color

The font size

Font weight
24

24

24

# 000000

15

normal
Button group

margin
0 0 8 and 24
button

The top margin

The right margin

fillers

color

The font size

Font weight

The text conversion

The background color
12

8

10

#387ef5

16

500

A capital

transparent

These default values are defined in the CustomAlert component. These values are used if the user does not provide them.

The different items for the CustomAlert component are.

support The value of use
modalVisible true|false Need to be To show/hide the modes
setModalVisible function The necessary Change the value of modalVisible
The title symbol optional Sets the title of the alert box
The message symbol optional Set the message
The android
{
    container: {
        backgroundColor: String
    },
    title: {
        color: String,
        fontFamily: String,
        fontSize: Number,
        fontWeight: String,
    },
    message: {
        color: String,
        fontFamily: String,
        fontSize: Number,
        fontWeight: String
    },
}
All fields are optional Styles your Android app’s prompt, title, and message
ios
{
    container: {
        backgroundColor: String
    },
    title: {
        color: String,
        fontFamily: String,
        fontSize: Number,
        fontWeight: String,
    },
    message: {
        color: String,
        fontFamily: String,
        fontSize: Number,
        fontWeight: String
    },
}
All fields are optional Set the styles of the iOS app prompt, title, and message.
button
[
  {
      text: String,
      func: Function,
      styles: {
        color: String,
        fontSize: Number,
        fontWeight: String,
        fontFamily: String,
        textTransform: String,
        backgroundColor: String
      }
  }
]
All fields are optional Sets the properties of the button. There are a few key points here.

  1. Android supports up to three buttons
  2. If no button text is provided, it uses OK, CANCEL, ASK ME LATER
  3. All buttons close the alert box and run the provided functions.
  4. If neither button is provided, it will default to an OK button.

Take a look at this code to see how CustomAlert defines the default values.

const CustomAlert = (props) => {

  const [androidDefaults, setAndroidDefaults] = useState({
    container: {
      backgroundColor: (props.android && props.android.container && props.android.container.backgroundColor) || '#FAFAFA',
    },
    title: {
      color: (props.android && props.android.title && props.android.title.color) || '#000000',
      fontFamily: (props.android && props.android.title && props.android.title.fontFamily) || 'initial',
      fontSize: (props.android && props.android.title && props.android.title.fontSize) || 22,
      fontWeight: (props.android && props.android.title && props.android.title.fontWeight) || 'bold',
    },
    message: {
      color: (props.android && props.android.message && props.android.message.color) || '#000000',
      fontFamily: (props.android && props.android.message && props.android.message.fontFamily) || 'initial',
      fontSize: (props.android && props.android.message && props.android.message.fontSize) || 15,
      fontWeight: (props.android && props.android.message && props.android.message.fontWeight) || 'normal',
    },
    button: {
      color: '#387ef5',
      fontFamily: 'initial',
      fontSize: 16,
      fontWeight: '500',
      textTransform: 'uppercase',
      backgroundColor: 'transparent',
    },
  });

  return (
    <Modal
        animationType="fade"
        transparent={true}
        visible={props.modalVisible}
        onRequestClose={() => {
          props.setModalVisible(false);
        }}
      >
        <Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => props.setModalVisible(false)} />
        <View style={styles.alertBox}>
        {
          Platform.OS === "ios" ? 
          null
          :
          <View style={[styles.androidAlertBox, androidDefaults.container]}>
            <Text style={[styles.androidTitle, androidDefaults.title]}>{props.title || 'Message'}</Text>
            <Text style={[styles.androidMessage, androidDefaults.message]}>{props.message || ''}</Text>
          </View>
        }
        </View>


      </Modal>
  )
}

Copy the code

Some styling is for layout, which is why we don’t provide options to change them. And these are declared in the StyleSheet object.

const styles = StyleSheet.create({ centeredView: { flex: 1, justifyContent: "center", alignItems: "center", marginTop: 22 }, button: { borderRadius: 20, padding: 10, elevation: 2 }, buttonOpen: { backgroundColor: "#F194FF", }, textStyle: { color: "white", fontWeight: "bold", textAlign: "center" }, iOSBackdrop: { backgroundColor: "#000000", opacity: Opacity opacity backdrop backdrop backdrop backdrop backdrop backdrop: {position: 'absolute', top: 0, left: 0, bottom: 0, right: 0 }, alertBox: { flex: 1, justifyContent: 'center', alignItems: 'center' }, androidAlertBox: { maxWidth: 280, width: '100%', margin: 48, elevation: 24, borderRadius: 2, }, androidTitle: { margin: 24, }, androidMessage: { marginLeft: 24, marginRight: 24, marginBottom: 24, }, androidButtonGroup: { marginTop: 0, marginRight: 0, marginBottom: 8, marginLeft: 24, }, androidButton: { marginTop: 12, marginRight: 8, }, androidButtonInner: { padding: 10, } });Copy the code

If we run this with a different setup, we get the following output.

It’s time to add the button to the alert dialog. Android comes in several specifications.

  1. The individual buttons are always OK
  2. The two buttons are CANCEL and OK
  3. The three buttons are ASK ME LATER, CANCEL and OK.
  4. A maximum of three buttons are supported
  5. Two buttons float on the right side of the box, while the third button floats on the left
  6. Long buttons are displayed on different lines

To satisfy all of these conditions, we decided to declare a separate component for a button group. Let’s call it AndroidButtonBox. Take a look at this code.

const AndroidButtonBox = () => {
    const [buttonLayoutHorizontal, setButtonLayoutHorizontal] = useState(1);
    const buttonProps = props.buttons && props.buttons.length > 0 ? props.buttons : [{}]

    return (
      <View style={[styles.androidButtonGroup, {
        flexDirection: buttonLayoutHorizontal === 1 ? "row" : "column",
      }]} onLayout={(e) => {
        if(e.nativeEvent.layout.height > 60)
          setButtonLayoutHorizontal(0);
      }}>
        {
          buttonProps.map((item, index) => {
              if(index > 2) return null;
              const alignSelfProperty = buttonProps.length > 2 && index === 0 && buttonLayoutHorizontal === 1 ?  'flex-start' : 'flex-end';
              let defaultButtonText = 'OK'
              if(buttonProps.length > 2){
                if(index === 0)
                  defaultButtonText = 'ASK ME LATER'
                else if(index === 1)
                  defaultButtonText = 'CANCEL';
              } else if (buttonProps.length === 2 && index === 0)
                defaultButtonText = 'CANCEL';
              return (
                <View style={[styles.androidButton, index === 0 && buttonLayoutHorizontal === 1 ? {flex: 1} : {}]}>
                  <Pressable onPress={() => {
                    props.setModalVisible(false)
                    if(item.func && typeof(item.func) === 'function')
                      item.func();
                  }} style={[{
                    alignSelf: alignSelfProperty, 

                  }]}>
                    <View style={[styles.androidButtonInner, {backgroundColor: (item.styles && item.styles.backgroundColor) || androidDefaults.button.backgroundColor}]}>
                      <Text
                        style={{
                          color: (item.styles && item.styles.color) || androidDefaults.button.color,
                          fontFamily: (item.styles && item.styles.fontFamily) || androidDefaults.button.fontFamily,
                          fontSize: (item.styles && item.styles.fontSize) || androidDefaults.button.fontSize,
                          fontWeight: (item.styles && item.styles.fontWeight) || androidDefaults.button.fontWeight,
                          textTransform: (item.styles && item.styles.textTransform) || androidDefaults.button.textTransform,
                        }}
                      >{item.text || defaultButtonText}</Text>
                    </View>
                  </Pressable>
                </View>
              )
            })

        }
      </View>
    );
  }

Copy the code

In this code, we declare a state variable, buttonLayoutHorizontal. This will be used to change the layout of the button group from columns to rows. If all buttons are short, they will be displayed in a single line.

The onLayout event is used to determine if the state variable needs to be changed. We then run a loop over the array of buttons provided through props and create buttons with the appropriate style.

The entire code (for Android) looks like this.

import React, { useState } from "react";
import { Alert, Modal, StyleSheet, Text, Pressable, View, Platform } from "react-native";
const CustomAlert = (props) => {

  const [androidDefaults, setAndroidDefaults] = useState({
    container: {
      backgroundColor: (props.android && props.android.container && props.android.container.backgroundColor) || '#FAFAFA',
    },
    title: {
      color: (props.android && props.android.title && props.android.title.color) || '#000000',
      fontFamily: (props.android && props.android.title && props.android.title.fontFamily) || 'initial',
      fontSize: (props.android && props.android.title && props.android.title.fontSize) || 22,
      fontWeight: (props.android && props.android.title && props.android.title.fontWeight) || 'bold',
    },
    message: {
      color: (props.android && props.android.message && props.android.message.color) || '#000000',
      fontFamily: (props.android && props.android.message && props.android.message.fontFamily) || 'initial',
      fontSize: (props.android && props.android.message && props.android.message.fontSize) || 15,
      fontWeight: (props.android && props.android.message && props.android.message.fontWeight) || 'normal',
    },
    button: {
      color: '#387ef5',
      fontFamily: 'initial',
      fontSize: 16,
      fontWeight: '500',
      textTransform: 'uppercase',
      backgroundColor: 'transparent',
    },
  });
  const AndroidButtonBox = () => {
    const [buttonLayoutHorizontal, setButtonLayoutHorizontal] = useState(1);
    const buttonProps = props.buttons && props.buttons.length > 0 ? props.buttons : [{}]

    return (
      <View style={[styles.androidButtonGroup, {
        flexDirection: buttonLayoutHorizontal === 1 ? "row" : "column",
      }]} onLayout={(e) => {
        if(e.nativeEvent.layout.height > 60)
          setButtonLayoutHorizontal(0);
      }}>
        {
          buttonProps.map((item, index) => {
              if(index > 2) return null;
              const alignSelfProperty = buttonProps.length > 2 && index === 0 && buttonLayoutHorizontal === 1 ?  'flex-start' : 'flex-end';
              let defaultButtonText = 'OK'
              if(buttonProps.length > 2){
                if(index === 0)
                  defaultButtonText = 'ASK ME LATER'
                else if(index === 1)
                  defaultButtonText = 'CANCEL';
              } else if (buttonProps.length === 2 && index === 0)
                defaultButtonText = 'CANCEL';
              return (
                <View style={[styles.androidButton, index === 0 && buttonLayoutHorizontal === 1 ? {flex: 1} : {}]}>
                  <Pressable onPress={() => {
                    props.setModalVisible(false)
                    if(item.func && typeof(item.func) === 'function')
                      item.func();
                  }} style={[{
                    alignSelf: alignSelfProperty, 

                  }]}>
                    <View style={[styles.androidButtonInner, {backgroundColor: (item.styles && item.styles.backgroundColor) || androidDefaults.button.backgroundColor}]}>
                      <Text
                        style={{
                          color: (item.styles && item.styles.color) || androidDefaults.button.color,
                          fontFamily: (item.styles && item.styles.fontFamily) || androidDefaults.button.fontFamily,
                          fontSize: (item.styles && item.styles.fontSize) || androidDefaults.button.fontSize,
                          fontWeight: (item.styles && item.styles.fontWeight) || androidDefaults.button.fontWeight,
                          textTransform: (item.styles && item.styles.textTransform) || androidDefaults.button.textTransform,
                        }}
                      >{item.text || defaultButtonText}</Text>
                    </View>
                  </Pressable>
                </View>
              )
            })

        }
      </View>
    );
  }
  return (
    <Modal
        animationType="fade"
        transparent={true}
        visible={props.modalVisible}
        onRequestClose={() => {
          props.setModalVisible(false);
        }}
      >
        <Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => props.setModalVisible(false)} />
        <View style={styles.alertBox}>
        {
          Platform.OS === "ios" ? 
          null
          :
          <View style={[styles.androidAlertBox, androidDefaults.container]}>
            <Text style={[styles.androidTitle, androidDefaults.title]}>{props.title || 'Message'}</Text>
            <Text style={[styles.androidMessage, androidDefaults.message]}>{props.message || ''}</Text>
            <AndroidButtonBox />
          </View>
        }
        </View>


      </Modal>
  )
}
const App = () => {
  const [modalVisible, setModalVisible] = useState(false);

  return (
    <View style={styles.centeredView}>
      <CustomAlert 
          modalVisible={modalVisible} 
          setModalVisible={setModalVisible}
          title={'Alert Title'}
          message={'This is some message'} 
          android={{
            container: {
              backgroundColor: 'yellow'
            },
            title: {
              color: 'red',
              fontFamily: 'Roboto',
              fontSize: 26,
              fontWeight: 'regular',
            },
            message: {
              color: 'blue',
              fontFamily: 'Roboto',
              fontSize: 16,
              fontWeight: 'regular',
            },
          }}
          buttons={[{
            text: 'no'
          },{
            text: 'Yes',
            func: () => {console.log('Yes Pressed')},
            styles: {
              color: '#FFFFFF',
              fontSize: 18,
              fontWeight: 'bold',
              fontFamily: 'Roboto',
              textTransform: 'none',
              backgroundColor: '#000000'
            }
          }]}
      />
      <View>
        <Pressable
          style={[styles.button, styles.buttonOpen]}
          onPress={() => setModalVisible(true)}
        >
          <Text style={styles.textStyle}>Show Modal</Text>
        </Pressable>
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  centeredView: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    marginTop: 22
  },

  button: {
    borderRadius: 20,
    padding: 10,
    elevation: 2
  },
  buttonOpen: {
    backgroundColor: "#F194FF",
  },
  textStyle: {
    color: "white",
    fontWeight: "bold",
    textAlign: "center"
  },

  iOSBackdrop: {
    backgroundColor: "#000000",
    opacity: 0.3
  },
  androidBackdrop: {
    backgroundColor: "#232f34",
    opacity: 0.4
  },
  backdrop: {
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0
  },
  alertBox: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  androidAlertBox: {
    maxWidth: 280,
    width: '100%',
    margin: 48,
    elevation: 24,
    borderRadius: 2,
  },
  androidTitle: {
    margin: 24,
  },
  androidMessage: {
    marginLeft: 24,
    marginRight: 24,
    marginBottom: 24,
  },
  androidButtonGroup: {
    marginTop: 0,
    marginRight: 0,
    marginBottom: 8,
    marginLeft: 24,
  },
  androidButton: {
    marginTop: 12,
    marginRight: 8,    
  },
  androidButtonInner: {
    padding: 10,

  }
});
export default App;

Copy the code

The rendered output for the different values of CustomAlert is.

IOS custom alert dialog box

The code for the iOS alert box will be similar to Android, with a change in style.

In iOS, all entities such as titles, messages, and buttons are centered. There can be any number of buttons. An iOS and Android CustomAlert box complete code is.

import React, { useState } from "react";
import { Alert, Modal, StyleSheet, Text, Pressable, View, Platform } from "react-native";
const CustomAlert = (props) => {

  const [androidDefaults, setAndroidDefaults] = useState({
    container: {
      backgroundColor: (props.android && props.android.container && props.android.container.backgroundColor) || '#FAFAFA',
    },
    title: {
      color: (props.android && props.android.title && props.android.title.color) || '#000000',
      fontFamily: (props.android && props.android.title && props.android.title.fontFamily) || 'initial',
      fontSize: (props.android && props.android.title && props.android.title.fontSize) || 22,
      fontWeight: (props.android && props.android.title && props.android.title.fontWeight) || 'bold',
    },
    message: {
      color: (props.android && props.android.message && props.android.message.color) || '#000000',
      fontFamily: (props.android && props.android.message && props.android.message.fontFamily) || 'initial',
      fontSize: (props.android && props.android.message && props.android.message.fontSize) || 15,
      fontWeight: (props.android && props.android.message && props.android.message.fontWeight) || 'normal',
    },
    button: {
      color: '#387ef5',
      fontFamily: 'initial',
      fontSize: 16,
      fontWeight: '500',
      textTransform: 'uppercase',
      backgroundColor: 'transparent',
    },
  });
  const [iOSDefaults, setIOSDefaults] = useState({
    container: {
      backgroundColor: (props.ios && props.ios.container && props.ios.container.backgroundColor) || '#F8F8F8',
    },
    title: {
      color: (props.ios && props.ios.title && props.ios.title.color) || '#000000',
      fontFamily: (props.ios && props.ios.title && props.ios.title.fontFamily) || 'initial',
      fontSize: (props.ios && props.ios.title && props.ios.title.fontSize) || 17,
      fontWeight: (props.ios && props.ios.title && props.ios.title.fontWeight) || '600',
    },
    message: {
      color: (props.ios && props.ios.message && props.ios.message.color) || '#000000',
      fontFamily: (props.ios && props.ios.message && props.ios.message.fontFamily) || 'initial',
      fontSize: (props.ios && props.ios.message && props.ios.message.fontSize) || 13,
      fontWeight: (props.ios && props.ios.message && props.ios.message.fontWeight) || 'normal',
    },
    button: {
      color: '#387ef5',
      fontFamily: 'initial',
      fontSize: 17,
      fontWeight: '500',
      textTransform: 'none',
      backgroundColor: 'transparent',
    },
  });
  const AndroidButtonBox = () => {
    const [buttonLayoutHorizontal, setButtonLayoutHorizontal] = useState(1);
    const buttonProps = props.buttons && props.buttons.length > 0 ? props.buttons : [{}]

    return (
      <View style={[styles.androidButtonGroup, {
        flexDirection: buttonLayoutHorizontal === 1 ? "row" : "column",
      }]} onLayout={(e) => {
        if(e.nativeEvent.layout.height > 60)
          setButtonLayoutHorizontal(0);
      }}>
        {
          buttonProps.map((item, index) => {
              if(index > 2) return null;
              const alignSelfProperty = buttonProps.length > 2 && index === 0 && buttonLayoutHorizontal === 1 ?  'flex-start' : 'flex-end';
              let defaultButtonText = 'OK'
              if(buttonProps.length > 2){
                if(index === 0)
                  defaultButtonText = 'ASK ME LATER'
                else if(index === 1)
                  defaultButtonText = 'CANCEL';
              } else if (buttonProps.length === 2 && index === 0)
                defaultButtonText = 'CANCEL';
              return (
                <View style={[styles.androidButton, index === 0 && buttonLayoutHorizontal === 1 ? {flex: 1} : {}]}>
                  <Pressable onPress={() => {
                    props.setModalVisible(false)
                    if(item.func && typeof(item.func) === 'function')
                      item.func();
                  }} style={[{
                    alignSelf: alignSelfProperty, 

                  }]}>
                    <View style={[styles.androidButtonInner, {backgroundColor: (item.styles && item.styles.backgroundColor) || androidDefaults.button.backgroundColor}]}>
                      <Text
                        style={{
                          color: (item.styles && item.styles.color) || androidDefaults.button.color,
                          fontFamily: (item.styles && item.styles.fontFamily) || androidDefaults.button.fontFamily,
                          fontSize: (item.styles && item.styles.fontSize) || androidDefaults.button.fontSize,
                          fontWeight: (item.styles && item.styles.fontWeight) || androidDefaults.button.fontWeight,
                          textTransform: (item.styles && item.styles.textTransform) || androidDefaults.button.textTransform,
                        }}
                      >{item.text || defaultButtonText}</Text>
                    </View>
                  </Pressable>
                </View>
              )
            })

        }
      </View>
    );
  }
  const IOSButtonBox = () => {
    const buttonProps = props.buttons && props.buttons.length > 0 ? props.buttons : [{}]
    const [buttonLayoutHorizontal, setButtonLayoutHorizontal] = useState(buttonProps.length === 2 ? 1 : 0);


    return (
      <View style={[styles.iOSButtonGroup, {
        flexDirection: buttonLayoutHorizontal === 1 ? "row" : "column",
      }]} onLayout={(e) => {
        if(e.nativeEvent.layout.height > 60)
          setButtonLayoutHorizontal(0);
      }}>
        {
          buttonProps.map((item, index) => {
              let defaultButtonText = 'OK'
              if(buttonProps.length > 2){
                if(index === 0)
                  defaultButtonText = 'ASK ME LATER'
                else if(index === 1)
                  defaultButtonText = 'CANCEL';
              } else if (buttonProps.length === 2 && index === 0)
                defaultButtonText = 'CANCEL';
              const singleButtonWrapperStyle = {}
              let singleButtonWeight = iOSDefaults.button.fontWeight;
              if(index === buttonProps.length - 1){
                  singleButtonWeight = '700';
              }
              if(buttonLayoutHorizontal === 1){
                singleButtonWrapperStyle.minWidth = '50%';
                if(index === 0){
                  singleButtonWrapperStyle.borderStyle = 'solid';
                  singleButtonWrapperStyle.borderRightWidth = 0.55;
                  singleButtonWrapperStyle.borderRightColor = '#dbdbdf';
                }

              }
              return (
                <View style={[styles.iOSButton, singleButtonWrapperStyle]}>
                  <Pressable onPress={() => {
                    props.setModalVisible(false)
                    if(item.func && typeof(item.func) === 'function')
                      item.func();
                  }}>
                    <View style={[styles.iOSButtonInner, {backgroundColor: (item.styles && item.styles.backgroundColor) || iOSDefaults.button.backgroundColor}]}>
                      <Text
                        style={{
                          color: (item.styles && item.styles.color) || iOSDefaults.button.color,
                          fontFamily: (item.styles && item.styles.fontFamily) || iOSDefaults.button.fontFamily,
                          fontSize: (item.styles && item.styles.fontSize) || iOSDefaults.button.fontSize,
                          fontWeight: (item.styles && item.styles.fontWeight) || singleButtonWeight,
                          textTransform: (item.styles && item.styles.textTransform) || iOSDefaults.button.textTransform,
                          textAlign: 'center'
                        }}
                      >{item.text || defaultButtonText}</Text>
                    </View>
                  </Pressable>
                </View>
              )
            })

        }
      </View>
    );
  }
  return (
    <Modal
        animationType="fade"
        transparent={true}
        visible={props.modalVisible}
        onRequestClose={() => {
          props.setModalVisible(false);
        }}
      >
        <Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => props.setModalVisible(false)} />
        <View style={styles.alertBox}>
        {
          Platform.OS === "ios" ? 
          <View style={[styles.iOSAlertBox, iOSDefaults.container]}>
            <Text style={[styles.iOSTitle, iOSDefaults.title]}>{props.title || 'Message'}</Text>
            <Text style={[styles.iOSMessage, iOSDefaults.message]}>{props.message || ''}</Text>
            <IOSButtonBox />
          </View>
          :
          <View style={[styles.androidAlertBox, androidDefaults.container]}>
            <Text style={[styles.androidTitle, androidDefaults.title]}>{props.title || 'Message'}</Text>
            <Text style={[styles.androidMessage, androidDefaults.message]}>{props.message || ''}</Text>
            <AndroidButtonBox />
          </View>
        }
        </View>


      </Modal>
  )
}
const App = () => {
  const [modalVisible, setModalVisible] = useState(false);

  return (
    <View style={styles.centeredView}>
      <CustomAlert 
          modalVisible={modalVisible} 
          setModalVisible={setModalVisible}
          title={'Alert Title'}
          message={'This is some message'} 
          android={{
            container: {
              backgroundColor: 'yellow'
            },
            title: {
              color: 'red',
              fontFamily: 'Roboto',
              fontSize: 26,
              fontWeight: 'regular',
            },
            message: {
              color: 'blue',
              fontFamily: 'Roboto',
              fontSize: 16,
              fontWeight: 'regular',
            },
          }}
          ios={{
            container: {
              backgroundColor: 'yellow'
            },
            title: {
              color: 'red',
              fontFamily: 'Roboto',
              fontSize: 26,
              fontWeight: 'regular',
            },
            message: {
              color: 'blue',
              fontFamily: 'Roboto',
              fontSize: 16,
              fontWeight: 'regular',
            },
          }}
          buttons={[{
            text: 'no'
          },{
            text: 'Yes',
            func: () => {console.log('Yes Pressed')},
            styles: {
              color: '#FFFFFF',
              fontSize: 18,
              fontWeight: 'bold',
              fontFamily: 'Roboto',
              textTransform: 'none',
              backgroundColor: '#000000'
            }
          }]}
      />
      <View>
        <Pressable
          style={[styles.button, styles.buttonOpen]}
          onPress={() => setModalVisible(true)}
        >
          <Text style={styles.textStyle}>Show Modal</Text>
        </Pressable>
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  centeredView: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    marginTop: 22
  },

  button: {
    borderRadius: 20,
    padding: 10,
    elevation: 2
  },
  buttonOpen: {
    backgroundColor: "#F194FF",
  },
  textStyle: {
    color: "white",
    fontWeight: "bold",
    textAlign: "center"
  },

  iOSBackdrop: {
    backgroundColor: "#000000",
    opacity: 0.3
  },
  androidBackdrop: {
    backgroundColor: "#232f34",
    opacity: 0.4
  },
  backdrop: {
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0
  },
  alertBox: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  androidAlertBox: {
    maxWidth: 280,
    width: '100%',
    margin: 48,
    elevation: 24,
    borderRadius: 2,
  },
  androidTitle: {
    margin: 24,
  },
  androidMessage: {
    marginLeft: 24,
    marginRight: 24,
    marginBottom: 24,
  },
  androidButtonGroup: {
    marginTop: 0,
    marginRight: 0,
    marginBottom: 8,
    marginLeft: 24,
  },
  androidButton: {
    marginTop: 12,
    marginRight: 8,    
  },
  androidButtonInner: {
    padding: 10,

  },

  iOSAlertBox: {
    maxWidth: 270,
    width: '100%',
    zIndex: 10,
    borderRadius: 13,
  },
  iOSTitle: {
    paddingTop: 12,
    paddingRight: 16,
    paddingBottom: 7,
    paddingLeft: 16,
    marginTop: 8,
    textAlign: "center",
  },
  iOSMessage: {
    paddingTop: 0,
    paddingRight: 16,
    paddingBottom: 21,
    paddingLeft: 16,
    textAlign: "center"
  },
  iOSButtonGroup: {
    marginRight: -0.55
  },
  iOSButton: {

    borderTopColor: '#dbdbdf',
    borderTopWidth: 0.55,
    borderStyle: 'solid',
  },
  iOSButtonInner: {
    minHeight: 44,
    justifyContent: 'center'
  }
});
export default App;

Copy the code

The render output for different CustomAlert values is.

Live demonstration

conclusion

In this article, we looked at alert dialogs in Android and iOS. We also see differences in their user interfaces, specifications, and attributes.

These alert dialogs have a clear purpose and should not be overused. Use information only when it is unavoidable to the user because it clogs the user interface.

There are many enhanced scopes in our custom alerts. You can change padding, margins, add background images, ICONS, SVG, and more. If you try this code out in your project, please let me know how it works in the comments. Thank you

The postHow to create a custom alert dialog in React Nativeappeared first onLogRocket Blog.