How to Build Custom Modal in React Native
Published: 2023-01-12 17:09:24

---
title: How to Build Custom Modal in React Native
published: true
date: 2023-01-12 16:43:06 UTC
tags: 
canonical_url: https://ezeavictor.hashnode.dev/how-to-build-custom-modal-in-react-native
---

Animation has become an integral part of mobile development, thanks to React native for its Animated API. In this, we will be building a custom modal with the help of react native animated API using just a button click.

A Modal component is a basic way to present content above an enclosing view.

**Prerequisite**

1. <mark>Node &gt;= 16.0.0</mark>

2. <mark>Expo CLI</mark>

To get started with the custom modal we need to generate a new expo project with the following command line.

`npx create-expo-app -t expo-template-blank-typescript`

cd into the project and run:

`yarn install @expo/vector-icons`

`yarn start or npm start`

**Screen Layout**

In the project, directory create a screen folder and a `tsx` file with the name `src/Screens/ModalScreen.tsx`

```
import React, { useState } from "react";
import { View, StyleSheet, Image, Text, TouchableOpacity } from "react-native";

const ModalScreen = () => {
  return (
    <View>
      <Text> Modal Screen</Text>
    </View>
  );
};

export default ModalScreen;

```

We also need to create the modal component, inside the project directory create the component folder `Components/Modals/ModalLayout.tsx`

```
import { View, Text, StyleSheet, Modal, Animated } from "react-native";
import React from "react";

const ModalLayout = () => {
  return (
    <View>
      <Text>Modal Layout</Text>
    </View>
  );
};

export default ModalLayout;

```

At the `ModalLayout.tsx` we need to import react hooks `useEffect, useState, useRef` to manage our states and also the `Animated` API together with `Modal` component from react-native.

The `Animated` library is designed to make animations fluid, powerful, and painless to build and maintain. `Animated` focuses on declarative relationships between inputs and outputs, configurable transforms in between, and `start`/`stop` methods to control time-based animation execution.

To calculate the scale value of our animation, we will use `Animated` value type `Animated.Value()` for single values.

```
  const scaleValue = useRef(new Animated.Value(0)).current;

```

A function toggleModal is created to help with the animation mechanism.

```
 const scaleValue = useRef(new Animated.Value(0)).current;
  const [showModal, setShowModal] = useState(visible);

  useEffect(() => {
    toggleModal();
  }, [visible]);

  const toggleModal = () => {
    if (visible) {
      setShowModal(true);
      Animated.spring(scaleValue, {
        toValue: 1,
        useNativeDriver: true,
      }).start();
    } else {
      setTimeout(() => setShowModal(false), 200);
      Animated.timing(scaleValue, {
        toValue: 0,
        duration: 300,
        useNativeDriver: true,
      }).start();
    }
  };

```

The full `ModalLayout.tsx` will look like this.

```
import { View, StyleSheet, Modal, Animated } from "react-native";
import React, { useState, useRef, useEffect } from "react";
import { COLORS } from "../../utils";

type Props = {
  visible: boolean;
  children: ReactNode;
};

const ModalLayout = ({ children, visible }: Props) => {
  const scaleValue = useRef(new Animated.Value(0)).current;
  const [showModal, setShowModal] = useState(visible);

  useEffect(() => {
    toggleModal();
  }, [visible]);

  const toggleModal = () => {
    if (visible) {
      setShowModal(true);
      Animated.spring(scaleValue, {
        toValue: 1,
        useNativeDriver: true,
      }).start();
    } else {
      setTimeout(() => setShowModal(false), 200);
      Animated.timing(scaleValue, {
        toValue: 0,
        duration: 300,
        useNativeDriver: true,
      }).start();
    }
  };

  return (
    <Modal transparent visible={showModal}>
      <View style={styles.container}>
        <Animated.View
          style={[styles.modalCon, { transform: [{ scale: scaleValue }] }]}
        >
          {children}
        </Animated.View>
      </View>
    </Modal>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: COLORS.modalbackgroundcolor,
  },

  modalCon: {
    backgroundColor: "white",
    paddingHorizontal: 20,
    paddingVertical: 30,
    borderRadius: 20,
    elevation: 20,
    width: "80%",
  },
});

export default ModalLayout;

```

Button Component

`create Button.tsx` inside the project directory `src/Components/Buttons/Button.tsx`

```
import { Text, TouchableOpacity, StyleSheet } from "react-native";
import React from "react";
import { COLORS } from "../../utils";

type ButtonProps = {
  onPress: () => void;
  title: string;
};

const Button = ({ onPress, title }: ButtonProps) => {
  return (
    <TouchableOpacity
      onPress={onPress}
      activeOpacity={0.9}
      style={styles.button}
    >
      <Text style={styles.title}>{title}</Text>
    </TouchableOpacity>
  );
};

const styles = StyleSheet.create({
  button: {
    height: 55,
    width: "65%",
    backgroundColor: COLORS.red,
    marginVertical: 10,
    justifyContent: "center",
    alignItems: "center",
    borderRadius: 5,
  },
  title: {
    color: COLORS.white,
    fontWeight: "bold",
    fontSize: 18,
  },
});

export default Button;

```

**Modal Screen**

In the modal screen import `ModalLayout.tsx` and `Button.tsx` components inside `ModalScreen.tsx` go ahead and import `Entypo` from `@expo/vector-icons`

```
import React, { useState } from "react";
import { View, StyleSheet, Image, Text, TouchableOpacity } from "react-native";

import { Button } from "../Components/Buttons";
import { Entypo } from "@expo/vector-icons";

import ModalLayout from "../Components/Modals/ModalLayout";

const ModalScreen = () => {
  const [visible, setVisible] = useState(false);

  return (
    <View style={styles.container}>
      <ModalLayout visible={visible}>
        <View style={{ alignItems: "center" }}>
          <View style={styles.header}>
            <TouchableOpacity onPress={() => setVisible(false)}>
              <Entypo
                name="cross"
                style={{ height: 30, width: 30 }}
                size={24}
                color="black"
              />
            </TouchableOpacity>
          </View>
        </View>
        <View style={{ alignItems: "center" }}>
          <Image
            source={require(".././assets/imageIcons/success.png")}
            style={{ height: 150, width: 150, marginVertical: 10 }}
          />
        </View>

        <Text style={{ marginVertical: 30, fontSize: 20, textAlign: "center" }}>
          Transaction Successful
        </Text>
      </ModalLayout>
      <Button title="Show Modal" onPress={() => setVisible(true)} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  header: {
    width: "100%",
    height: 40,
    alignItems: "flex-end",
    justifyContent: "center",
  },
});

export default ModalScreen;

```

create `src/utils/Colors.tsx` in the project directory

```
const COLORS = {
  modalbackgroundcolor: "rgba(0,0,0,0.5)",
  modalbackgroundContainerColor: "white",
  white: "#FFFFFF",
  red: "#FF0000",
};

export default COLORS;

```

The custom modal should look like this

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673541422529/59d12220-b37c-47e0-b374-b80e7cd9c605.png)

GitHub link to the tutorial: [Github Link](https://github.com/Victorvikson1996/React-Native-Custom-Modal-)

**Conclusion**

The tutorial completes how we can use Animated API and Modal component from react-native to build our custom modal in our mobile projects. kindly share this article and follow on [Twitter](https://twitter.com/Victorebukaezea)