├── .DS_Store
├── .babelrc
├── .expo
├── README.md
└── settings.json
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc
├── CONTRIBUTING.md
├── LICENSE
├── README-legacy.md
├── README.md
├── components
├── BaseToast.tsx
├── ErrorToast.tsx
├── InfoToast.tsx
├── SuccessToast.tsx
├── ToastManager.tsx
├── WarnToast.tsx
└── styles.ts
├── config
└── theme.ts
├── index.ts
├── package-lock.json
├── package.json
├── sample
├── .gitignore
├── Another.js
├── App.js
├── app.json
├── assets
│ ├── adaptive-icon.png
│ ├── favicon.png
│ ├── icon.png
│ └── splash-icon.png
├── index.js
├── metro.config.js
├── package-lock.json
├── package.json
└── tsconfig.json
├── tsconfig.json
└── utils
├── defaultConfig.ts
├── defaultProps.ts
├── helpers.ts
└── interfaces.ts
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zahidalidev/toastify-react-native/2410fc40480a56b312f05f27652e61db7fa0cf26/.DS_Store
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["module:metro-react-native-babel-preset"]
3 | }
4 |
--------------------------------------------------------------------------------
/.expo/README.md:
--------------------------------------------------------------------------------
1 | > Why do I have a folder named ".expo" in my project?
2 |
3 | The ".expo" folder is created when an Expo project is started using "expo start" command.
4 |
5 | > What do the files contain?
6 |
7 | - "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds.
8 | - "packager-info.json": contains port numbers and process PIDs that are used to serve the application to the mobile device/simulator.
9 | - "settings.json": contains the server configuration that is used to serve the application manifest.
10 |
11 | > Should I commit the ".expo" folder?
12 |
13 | No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine.
14 |
15 | Upon project creation, the ".expo" folder is already added to your ".gitignore" file.
16 |
--------------------------------------------------------------------------------
/.expo/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "hostType": "lan",
3 | "lanType": "ip",
4 | "dev": true,
5 | "minify": false,
6 | "urlRandomness": null,
7 | "https": false
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | *.log
3 | npm-debug.log
4 |
5 | # Runtime data
6 | tmp
7 | build
8 | dist
9 |
10 | # Dependency directory
11 | node_modules
12 | react-native-toast-message-main
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | *.log
3 | npm-debug.log
4 |
5 | # Dependency directory
6 | node_modules
7 |
8 | # Runtime data
9 | tmp
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .expo
2 |
3 | /node_modules
4 | /build
5 |
6 | **/package-lock.json
7 |
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "endOfLine": "lf",
4 | "tabWidth": 2,
5 | "indentStyle": "space",
6 | "useTabs": false,
7 | "arrowParens": "always",
8 | "bracketSameLine": false,
9 | "singleQuote": true,
10 | "semi": false,
11 | "bracketSpacing": true,
12 | "jsxSingleQuote": true
13 | }
14 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍
4 |
5 | When contributing to this repository, please first discuss the change you wish to make via issue before making a change.
6 |
7 | Please note we have a code of conduct, please follow it in all your interactions with the project.
8 |
9 | ## General Guidelines
10 |
11 | - Ensure that nothing gets broken. You can use the sample project for that
12 | - Use prettier before committing (`npm run prettier`)
13 | - When solving a bug, please provide the steps to reproduce it (codesandbox is our best friend for that)
14 | - Keep it chill 👌
15 |
16 | ## Setup
17 |
18 | ### Pre-requisites
19 | - _Node:_ `>=16.0.0`
20 | - _npm_ or _yarn_
21 |
22 | ### Install
23 |
24 | Clone the repository and create a local branch:
25 |
26 | ```sh
27 | git clone https://github.com/zahidalidev/toastify-react-native.git
28 | cd toastify-react-native
29 |
30 | git checkout -b my-branch
31 | ```
32 |
33 | Install dependencies:
34 |
35 | ```sh
36 | npm install
37 | ```
38 |
39 | ## Developing
40 |
41 | The library doesn't use a state management library like Redux or MobX to dispatch notifications. Instead, it uses a singleton pattern with refs.
42 |
43 | ### Testing with the Sample Project
44 |
45 | We've included a sample project to help you test your changes in a real React Native environment:
46 |
47 | 1. First, install the dependencies for the main package:
48 | ```sh
49 | npm install
50 | ```
51 |
52 | 2. Navigate to the sample project directory:
53 | ```sh
54 | cd sample
55 | ```
56 |
57 | 3. Install the sample project dependencies:
58 | ```sh
59 | npm install
60 | ```
61 |
62 | 4. Start the sample project:
63 | ```sh
64 | npm start
65 | ```
66 |
67 | 5. Use Expo to run the app on your device or simulator:
68 | ```sh
69 | # For iOS
70 | npm run ios
71 |
72 | # For Android
73 | npm run android
74 | ```
75 |
76 | The sample project is set up to use the local version of toastify-react-native, so any changes you make to the library code will be reflected in the sample app (If not try reloading it).
77 |
78 | ### Project structure
79 |
80 | #### Main package
81 |
82 | - `/components`: Contains all the toast components
83 | - `/utils`: Helper functions, interfaces, and default configurations
84 | - `/config`: Theme and styling configurations
85 | - `index.ts`: Main entry point for the package
86 |
87 | #### Sample project
88 |
89 | The sample project in the `/sample` directory lets you test your changes in a real React Native environment. It's a great way to verify that your changes work as expected before submitting a pull request.
90 |
91 | ## Making Changes
92 |
93 | 1. Make your changes to the library code
94 | 2. Test your changes using the sample project
95 | 3. Run prettier to format your code:
96 | ```sh
97 | npm run prettier
98 | ```
99 | 4. Commit your changes with a descriptive commit message
100 | 5. Push your changes to your fork
101 | 6. Submit a pull request
102 |
103 | ## License
104 |
105 | By contributing, you agree that your contributions will be licensed under its MIT License.
106 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Zahid Ali
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README-legacy.md:
--------------------------------------------------------------------------------
1 | # toastify-react-native
2 |
3 | [](https://badge.fury.io/js/toastify-react-native)
4 |
5 | 🎉 toastify-react-native allows you to add notifications to your react-native app (ios, android) with ease. No more nonsense!
6 |
7 | ## Demo
8 |
9 | ## [View examples on snack.expo.io](https://snack.expo.io/@zahidalidev/toastify-react-native)
10 |
11 | https://user-images.githubusercontent.com/46484008/190667640-02a77a0c-8aed-4dc9-a1d3-abf9cb5b3c0a.mp4
12 |
13 | ## Features
14 |
15 | - Smooth enter/exit animations
16 | - Plain simple and flexible APIs
17 | - Resize itself correctly on device rotation
18 | - Swipeable
19 | - Easy to set up for real, you can make it work in less than 10sec!
20 | - Super easy to customize
21 | - RTL support
22 | - Swipe to close 👌
23 | - Can choose swipe direction
24 | - Super easy to use an animation of your choice. Works well with animate.css for example
25 | - Define behavior per toast
26 | - Pause toast by click on the toast 👁
27 | - Fancy progress bar to display the remaining time
28 | - Possibility to update a toast
29 | - You can control the progress bar a la nprogress 😲
30 | - You can display multiple toast at the same time
31 | - Dark and light mode 🌒
32 | - And much more !
33 |
34 | ## Installation
35 |
36 | ```sh
37 | $ npm install toastify-react-native
38 | ```
39 |
40 | ### Required Dependencies
41 |
42 | This package depends on the following packages which need to be installed separately:
43 |
44 | ```sh
45 | $ npm install react-native-modal react-native-vector-icons
46 | ```
47 |
48 | ## A complete example
49 |
50 | ### App.js
51 |
52 | ```javascript
53 | import React from 'react'
54 | import { StyleSheet, View, TouchableOpacity, Text } from 'react-native'
55 | import ToastManager, { Toast } from 'toastify-react-native'
56 |
57 | import Another from './Another'
58 |
59 | const App = () => {
60 | const showToasts = () => {
61 | Toast.success('Promised is resolved')
62 | }
63 |
64 | return (
65 |
66 |
67 |
68 |
77 | SHOW SOME AWESOMENESS!
78 |
79 |
80 | )
81 | }
82 |
83 | const styles = StyleSheet.create({
84 | container: {
85 | flex: 1,
86 | backgroundColor: '#fff',
87 | alignItems: 'center',
88 | justifyContent: 'center',
89 | },
90 | })
91 |
92 | export default App
93 | ```
94 |
95 | ### Another.js
96 |
97 | ```javascript
98 | import React from 'react'
99 | import { StyleSheet, View, TouchableOpacity, Text } from 'react-native'
100 | import { Toast } from 'toastify-react-native'
101 | const Another = () => (
102 |
103 | Toast.info('Lorem ipsum info', 'bottom')}
105 | style={styles.buttonStyle}
106 | >
107 | SHOW SOME AWESOMENESS!
108 |
109 |
110 | )
111 |
112 | const styles = StyleSheet.create({
113 | container: {
114 | backgroundColor: '#fff',
115 | alignItems: 'center',
116 | justifyContent: 'center',
117 | },
118 | buttonStyle: {
119 | marginTop: 10,
120 | backgroundColor: 'white',
121 | borderColor: 'green',
122 | borderWidth: 2,
123 | padding: 10,
124 | },
125 | })
126 |
127 | export default Another
128 | ```
129 |
130 | For a more complex example take a look at the `/example` directory.
131 |
132 | ## Available props
133 |
134 | | Name | Type | Default | Description |
135 | | --------------------------- | ---------------------------------------------- | -------------- | ----------------------------------------------- |
136 | | width | number or 'auto' | 256 | Width of toast |
137 | | minHeight | number or 'auto' | 68 | Minimum height of the toast |
138 | | style | any | null | Style applied to the toast |
139 | | textStyle | any | null | Style applied to the toast content |
140 | | position | top, center or bottom | top | Position of toast |
141 | | positionValue | number | 50 | position value of toast |
142 | | duration | number | 3000 | The display time of toast. |
143 | | animationStyle | slideInOut, upInUpOut, rightInOut or zoomInOut | slideInOut | The animation style of toast |
144 | | animationIn | string or object | 'slideInRight' | Toast show animation |
145 | | animationOut | string or object | 'slideOutLeft' | Toast hide animation |
146 | | animationInTiming | number | 300 | Timing for the Toast show animation (in ms) |
147 | | animationOutTiming | number | 300 | Timing for the toast hide animation (in ms) |
148 | | backdropTransitionInTiming | number | 300 | The backdrop show timing (in ms) |
149 | | backdropTransitionOutTiming | number | 300 | The backdrop hide timing (in ms) |
150 | | hasBackdrop | bool | false | Render the backdrop |
151 | | backdropColor | string | 'black' | The backdrop background color |
152 | | backdropOpacity | number | 0.2 | The backdrop opacity when the toast is visible |
153 | | showCloseIcon | boolean | true | Shows the close icon in the right corner |
154 | | showProgressBar | boolean | true | Shows the progress bar in the toast |
155 | | isRTL | boolean | false | Right to left support for languages like Arabic |
156 |
157 | ## Available animations
158 |
159 | Take a look at [react-native-animatable](https://github.com/oblador/react-native-animatable) to see the dozens of animations available out-of-the-box.
160 |
161 | ## Acknowledgements
162 |
163 | Pull requests, feedbacks and suggestions are welcome!
164 |
165 | ## License
166 |
167 | toastify-react-native is [MIT licensed](https://github.com/zahidalidev/toastify-react-native/blob/master/LICENSE) .
168 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # toastify-react-native
2 |
3 | [](https://badge.fury.io/js/toastify-react-native)
4 |
5 | 🎉 toastify-react-native allows you to add notifications to your React Native app (iOS, Android) with ease. No more nonsense!
6 |
7 | ## Table of Contents
8 |
9 | - [Demo](#demo)
10 | - [Features](#features)
11 | - [Installation](#installation)
12 | - [Basic Usage](#basic-usage)
13 | - [Advanced Usage](#advanced-usage)
14 | - [Modal Behavior](#modal-behavior) 👈 **NEW**
15 | - [Available Props](#available-props)
16 | - [Custom Components](#custom-components)
17 | - [Customizing Icons](#customizing-icons)
18 | - [API Reference](#api-reference)
19 | - [Upgrading from v6.x](#upgrading-from-v6x)
20 | - [Contributing](#contributing)
21 | - [License](#license)
22 |
23 | ## Demo
24 |
25 | ## [View examples on snack.expo.io](https://snack.expo.io/@zahidalidev/toastify-react-native)
26 |
27 |
28 |
29 | ## Features
30 |
31 | - 🚀 **Simple API**: Easy to use with minimal setup
32 | - 🎨 **Highly customizable**: Customize colors, icons, animations, and more
33 | - 🧩 **Custom components**: Create your own toast components
34 | - 🎭 **Custom icons**: Use different icon families, custom icon components, or JSX elements
35 | - 📱 **Responsive**: Adapts to different screen sizes
36 | - 🌓 **Dark & Light mode**: Built-in theme support
37 | - 🔄 **RTL support**: Right-to-left language support
38 | - ⏱️ **Progress bar**: Visual indicator of toast duration
39 | - 🖐️ **Interactive**: Pause on touch, resume on release
40 | - 🔄 **Animation options**: Choose from different animation styles
41 | - 📝 **Multiple lines**: Support for title and description
42 | - 🔍 **TypeScript support**: Full type definitions included
43 | - ✨ **Smooth animations**: Beautiful enter/exit animations
44 | - ⚡ **Quick setup**: Get up and running in less than 10 seconds!
45 | - 🎛️ **Per-toast behavior**: Define different behaviors for each toast
46 | - 📊 **Progress control**: Control the progress bar like nprogress
47 | - 🔧 **Super easy to customize**: Modify every aspect to match your app's design
48 | - 🎭 **And much more!**: Discover all the possibilities!
49 |
50 | ## Installation
51 |
52 | ```sh
53 | npm install toastify-react-native
54 | # or
55 | yarn add toastify-react-native
56 | ```
57 |
58 | ### Required Dependencies
59 |
60 | This package requires `react-native-vector-icons`:
61 |
62 | ```sh
63 | npm install react-native-vector-icons
64 | # or
65 | yarn add react-native-vector-icons
66 | ```
67 |
68 | Follow the [react-native-vector-icons installation guide](https://github.com/oblador/react-native-vector-icons#installation) to complete the setup for your platform.
69 |
70 | ## Basic Usage
71 |
72 | ```jsx
73 | import React from 'react'
74 | import { View, Button } from 'react-native'
75 | import ToastManager, { Toast } from 'toastify-react-native'
76 |
77 | export default function App() {
78 | return (
79 |
80 |
111 | )
112 | }
113 | ```
114 |
115 | ## Advanced Usage
116 |
117 | ### Custom Configuration
118 |
119 | ```jsx
120 | import React from 'react'
121 | import { View, Button, Text } from 'react-native'
122 | import ToastManager, { Toast } from 'toastify-react-native'
123 |
124 | // Custom toast configuration
125 | const toastConfig = {
126 | success: (props) => (
127 |
128 | {props.text1}
129 | {props.text2 && {props.text2}}
130 |
131 | ),
132 | // Override other toast types as needed
133 | }
134 |
135 | export default function App() {
136 | return (
137 |
138 | {
141 | Toast.show({
142 | type: 'success',
143 | text1: 'Main message',
144 | text2: 'Secondary message',
145 | position: 'bottom',
146 | visibilityTime: 4000,
147 | autoHide: true,
148 | onPress: () => console.log('Toast pressed'),
149 | onShow: () => console.log('Toast shown'),
150 | onHide: () => console.log('Toast hidden'),
151 | })
152 | }}
153 | />
154 |
155 | {/* Toast provider with custom config */}
156 |
157 |
158 | )
159 | }
160 | ```
161 |
162 | ### Toast Positions
163 |
164 | ```jsx
165 | Toast.success('Top toast', 'top') // default
166 | Toast.error('Center toast', 'center')
167 | Toast.info('Bottom toast', 'bottom')
168 | ```
169 |
170 | ### Customizing Individual Toasts
171 |
172 | ```jsx
173 | Toast.show({
174 | type: 'success',
175 | text1: 'Custom Toast',
176 | text2: 'With many options',
177 | position: 'bottom',
178 | visibilityTime: 5000,
179 | autoHide: true,
180 | backgroundColor: '#333',
181 | textColor: '#fff',
182 | iconColor: '#4CAF50',
183 | iconSize: 24,
184 | progressBarColor: '#4CAF50',
185 | theme: 'dark',
186 | })
187 | ```
188 |
189 | ## Modal Behavior
190 |
191 | The `useModal` prop controls whether the toast uses a modal overlay that blocks interaction with the background screen. This is particularly important when working with modals in your app.
192 |
193 | ### Why is this important?
194 |
195 | - **With Modal (`useModal: true`)**: The toast appears with a modal overlay, making the background screen non-interactive. This ensures the toast is always visible, even over other modals, but prevents users from interacting with content behind it.
196 |
197 | - **Without Modal (`useModal: false`)**: The toast appears without blocking interaction with the background screen. Users can still interact with your app while the toast is displayed, but the toast might not appear over other modal components.
198 |
199 | ### Usage Examples
200 |
201 | #### Setting at ToastManager level (affects all toasts)
202 |
203 | ```jsx
204 | // All toasts will use modal behavior by default
205 |
206 |
207 | // All toasts will NOT use modal behavior by default
208 |
209 | ```
210 |
211 | #### Setting for individual toasts with Toast.show()
212 |
213 | ```jsx
214 | // This toast will use modal behavior
215 | Toast.show({
216 | type: 'success',
217 | text1: 'Using Modal',
218 | text2: 'Background is not interactive',
219 | useModal: true,
220 | })
221 |
222 | // This toast will NOT use modal behavior
223 | Toast.show({
224 | type: 'error',
225 | text1: 'No Modal',
226 | text2: 'Background remains interactive',
227 | useModal: false,
228 | })
229 | ```
230 |
231 | #### Using with shorthand methods
232 |
233 | ```jsx
234 | // Success toast with modal behavior
235 | Toast.success(
236 | 'Success with Modal!',
237 | 'bottom', // position
238 | undefined, // icon
239 | undefined, // iconFamily
240 | true, // useModal
241 | )
242 |
243 | // Error toast without modal behavior
244 | Toast.error(
245 | 'Error without Modal!',
246 | 'bottom', // position
247 | undefined, // icon
248 | undefined, // iconFamily
249 | false, // useModal
250 | )
251 | ```
252 |
253 | #### Conditional usage based on context
254 |
255 | ```jsx
256 | // Inside a modal component
257 | const showToastInModal = () => {
258 | Toast.show({
259 | type: 'info',
260 | text1: 'Modal Context',
261 | text2: 'This toast appears over the modal',
262 | useModal: true, // Ensure it appears over the modal
263 | })
264 | }
265 |
266 | // In regular app context
267 | const showRegularToast = () => {
268 | Toast.show({
269 | type: 'success',
270 | text1: 'Regular Context',
271 | text2: 'Allow interaction with the app',
272 | useModal: false, // Allow background interaction
273 | })
274 | }
275 | ```
276 |
277 | ## Available Props
278 |
279 | ### ToastManager Props
280 |
281 | | Prop | Type | Default | Description |
282 | | --------------- | ----------------------------- | ---------- | -------------------------------------------------- |
283 | | width | number \| string \| 'auto' | '90%' | Width of the toast |
284 | | minHeight | number \| string \| 'auto' | 61 | Minimum height of the toast |
285 | | style | StyleProp | {} | Custom style for the toast container |
286 | | textStyle | StyleProp | {} | Custom style for the toast text |
287 | | theme | 'light' \| 'dark' | 'light' | Theme of the toast |
288 | | animationStyle | 'none' \| 'slide' \| 'fade' | 'fade' | Animation style for the toast |
289 | | position | 'top' \| 'center' \| 'bottom' | 'top' | Position of the toast |
290 | | duration | number | 3000 | Duration in ms before the toast disappears |
291 | | showCloseIcon | boolean | true | Whether to show the close icon |
292 | | showProgressBar | boolean | true | Whether to show the progress bar |
293 | | isRTL | boolean | false | Right-to-left support |
294 | | topOffset | number | 40 | Distance from the top when position is 'top' |
295 | | bottomOffset | number | 40 | Distance from the bottom when position is 'bottom' |
296 | | iconSize | number | 22 | Size of the icon |
297 | | iconFamily | string | 'Ionicons' | Default icon family to use |
298 | | icons | object | undefined | Custom default icons for each toast type |
299 | | config | ToastConfig | undefined | Custom toast components configuration |
300 | | useModal | boolean | true | Whether to use modal overlay for toasts |
301 |
302 | ### Toast.show() Options
303 |
304 | | Option | Type | Default | Description |
305 | | ---------------- | ----------------------------------------------------- | --------- | ------------------------------------------- |
306 | | type | 'success' \| 'error' \| 'info' \| 'warn' \| 'default' | 'default' | Type of toast |
307 | | text1 | string | '' | Main text |
308 | | text2 | string | undefined | Secondary text |
309 | | position | 'top' \| 'center' \| 'bottom' | 'top' | Position of the toast |
310 | | visibilityTime | number | 3000 | Duration in ms before the toast disappears |
311 | | autoHide | boolean | true | Whether the toast should hide automatically |
312 | | onShow | () => void | undefined | Callback when toast is shown |
313 | | onHide | () => void | undefined | Callback when toast is hidden |
314 | | onPress | () => void | undefined | Callback when toast is pressed |
315 | | progressBarColor | string | undefined | Color of the progress bar |
316 | | backgroundColor | string | undefined | Background color of the toast |
317 | | textColor | string | undefined | Color of the text |
318 | | icon | string \| ReactNode | undefined | Custom icon name or component |
319 | | iconFamily | string | undefined | Icon family for the icon |
320 | | iconColor | string | undefined | Color of the icon |
321 | | iconSize | number | undefined | Size of the icon |
322 | | theme | 'light' \| 'dark' | undefined | Theme of the toast |
323 | | useModal | boolean | undefined | Whether to use modal overlay for this toast |
324 |
325 | ## Custom Components
326 |
327 | You can create your own toast components by providing a custom configuration:
328 |
329 | ```jsx
330 | import React from 'react'
331 | import { View, Text, StyleSheet } from 'react-native'
332 | import ToastManager, { Toast } from 'toastify-react-native'
333 | import Icon from 'react-native-vector-icons/MaterialIcons'
334 |
335 | const CustomToast = ({ text1, text2, hide }) => {
336 | return (
337 |
338 |
339 |
340 | {text1}
341 | {text2 && {text2}}
342 |
343 |
344 |
345 | )
346 | }
347 |
348 | const styles = StyleSheet.create({
349 | customToast: {
350 | width: '90%',
351 | backgroundColor: '#673AB7',
352 | borderRadius: 10,
353 | padding: 16,
354 | flexDirection: 'row',
355 | alignItems: 'center',
356 | shadowColor: '#000',
357 | shadowOffset: { width: 0, height: 2 },
358 | shadowOpacity: 0.25,
359 | shadowRadius: 3.84,
360 | elevation: 5,
361 | },
362 | textContainer: {
363 | flex: 1,
364 | marginLeft: 12,
365 | },
366 | title: {
367 | color: '#fff',
368 | fontWeight: 'bold',
369 | fontSize: 16,
370 | },
371 | message: {
372 | color: '#fff',
373 | fontSize: 14,
374 | marginTop: 4,
375 | },
376 | })
377 |
378 | export default function App() {
379 | const toastConfig = {
380 | custom: (props) => ,
381 | }
382 |
383 | return (
384 |
385 | {
388 | Toast.show({
389 | type: 'custom',
390 | text1: 'Custom Component',
391 | text2: 'This is a fully custom toast component!',
392 | })
393 | }}
394 | />
395 |
396 |
397 |
398 | )
399 | }
400 | ```
401 |
402 | ## Customizing Icons
403 |
404 | ```jsx
405 | // Different icon name from the default family
406 | Toast.show({
407 | type: 'success',
408 | text1: 'Different Icon',
409 | text2: 'Using a different icon name',
410 | icon: 'check', // Different icon name according to default icon family
411 | })
412 |
413 | // Using a different icon family
414 | Toast.show({
415 | type: 'error',
416 | text1: 'FontAwesome Icon',
417 | text2: 'Using a different icon family',
418 | icon: 'exclamation-circle',
419 | iconFamily: 'FontAwesome',
420 | })
421 |
422 | // Using a custom React component as icon
423 | const CustomIcon = ({ color }) => (
424 |
434 |
435 |
436 | )
437 |
438 | Toast.show({
439 | type: 'info',
440 | text1: 'Custom Component',
441 | text2: 'Using a custom React component as icon',
442 | icon: ,
443 | })
444 |
445 | // Using JSX directly as icon
446 | Toast.show({
447 | type: 'success',
448 | text1: 'JSX Icon',
449 | text2: 'Using JSX directly as icon',
450 | icon: (
451 |
452 |
453 |
459 |
460 | ),
461 | })
462 | ```
463 |
464 | ```jsx
465 | // Setting default icons at the ToastManager level
466 |
483 | ```
484 |
485 | ## API Reference
486 |
487 | ### Toast Functions
488 |
489 | - `Toast.show(options)`: Show a toast with custom options
490 | - `Toast.success(message, position?, icon?, iconFamily?, useModal?)`: Show a success toast
491 | - `Toast.error(message, position?, icon?, iconFamily?, useModal?)`: Show an error toast
492 | - `Toast.info(message, position?, icon?, iconFamily?, useModal?)`: Show an info toast
493 | - `Toast.warn(message, position?, icon?, iconFamily?, useModal?)`: Show a warning toast
494 | - `Toast.hide()`: Hide the current toast
495 |
496 | ## Upgrading from v6.x
497 |
498 | If you're upgrading from version 6.x, please note the following changes:
499 |
500 | - The animation system has been simplified to use React Native's built-in Modal animations
501 | - Some props have been removed or renamed for clarity
502 | - The styling system has been improved for better customization
503 | - Custom components now receive more props for better control
504 |
505 | For users of v6.x and below, refer to the [legacy documentation](./README-legacy.md).
506 |
507 | ## Contributing
508 |
509 | We welcome contributions to make toastify-react-native even better!
510 |
511 | - Check out our [contribution guidelines](./CONTRIBUTING.md) for details on the process
512 | - Have questions? Open an issue or join the discussion
513 | - Found a bug? Submit a pull request
514 | - Have a feature request? Open an issue
515 |
516 | Thank you to all our contributors!
517 |
518 | ## License
519 |
520 | toastify-react-native is [MIT licensed](https://github.com/zahidalidev/toastify-react-native/blob/master/LICENSE).
521 |
--------------------------------------------------------------------------------
/components/BaseToast.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, ReactNode } from 'react';
2 | import { View, Text, StyleSheet, Animated, TouchableOpacity, StyleProp, ViewStyle, DimensionValue } from 'react-native';
3 | import Icon from 'react-native-vector-icons/Ionicons';
4 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
5 | import FontAwesome from 'react-native-vector-icons/FontAwesome';
6 | import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
7 | import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
8 | import Entypo from 'react-native-vector-icons/Entypo';
9 | import Feather from 'react-native-vector-icons/Feather';
10 | import AntDesign from 'react-native-vector-icons/AntDesign';
11 | import Octicons from 'react-native-vector-icons/Octicons';
12 | import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons';
13 |
14 | import { Colors } from '../config/theme';
15 | import { SCALE } from '../utils/helpers';
16 |
17 | // Map of icon families to their components
18 | const IconFamilies = {
19 | Ionicons: Icon,
20 | MaterialIcons,
21 | FontAwesome,
22 | FontAwesome5,
23 | MaterialCommunityIcons,
24 | Entypo,
25 | Feather,
26 | AntDesign,
27 | Octicons,
28 | SimpleLineIcons
29 | };
30 |
31 | interface BaseToastProps {
32 | icon?: string | ReactNode;
33 | iconFamily?: keyof typeof IconFamilies | string;
34 | text1?: string;
35 | text2?: string;
36 | onPress?: () => void;
37 | hide?: () => void;
38 | backgroundColor?: string;
39 | textColor?: string;
40 | iconColor?: string;
41 | iconSize?: number;
42 | showProgressBar?: boolean;
43 | progressBarColor?: string;
44 | barWidth?: Animated.Value;
45 | isRTL?: boolean;
46 | showCloseIcon?: boolean;
47 | duration?: number;
48 | testID?: string;
49 | width?: number | string;
50 | minHeight?: number | string;
51 | style?: StyleProp;
52 | theme?: 'light' | 'dark';
53 | }
54 |
55 | const BaseToast = ({
56 | icon = 'checkmark-circle',
57 | iconFamily = 'Ionicons',
58 | text1,
59 | text2,
60 | onPress,
61 | hide,
62 | backgroundColor,
63 | textColor,
64 | iconColor = Colors.success,
65 | iconSize = SCALE(22),
66 | showProgressBar = true,
67 | progressBarColor,
68 | barWidth: externalBarWidth,
69 | isRTL = false,
70 | showCloseIcon = true,
71 | duration = 3000,
72 | testID = 'toast-base',
73 | width,
74 | minHeight,
75 | style,
76 | theme = 'light',
77 | }: BaseToastProps) => {
78 | // Use a local animated value if no external one is provided
79 | const localBarWidth = React.useRef(new Animated.Value(100)).current;
80 | const barWidth = externalBarWidth || localBarWidth;
81 |
82 | // Set background and text colors based on theme if not explicitly provided
83 | const bgColor = backgroundColor || Colors[theme].back;
84 | const txtColor = textColor || Colors[theme].text;
85 |
86 | // Start progress bar animation if none is provided externally
87 | useEffect(() => {
88 | if (!externalBarWidth && showProgressBar) {
89 | // Set initial value
90 | localBarWidth.setValue(isRTL ? 0 : 100);
91 |
92 | // Start the animation
93 | Animated.timing(localBarWidth, {
94 | toValue: isRTL ? 100 : 0,
95 | duration,
96 | useNativeDriver: false
97 | }).start();
98 | }
99 | }, [externalBarWidth, showProgressBar, isRTL, duration, localBarWidth]);
100 |
101 | const rtlContentStyle = isRTL ? { flexDirection: 'row-reverse' as 'row-reverse' } : {};
102 | const rtlHideButtonStyle = isRTL ? { right: undefined, left: SCALE(3.2) } : {};
103 | const rtlIconWrapperStyle = isRTL ? { marginRight: 0, marginLeft: SCALE(8) } : {};
104 | const rtlTextStyle = isRTL ? { textAlign: 'right' as 'right' } : {};
105 |
106 | // Adjust margin based on RTL
107 | const textMarginStyle = isRTL
108 | ? { marginLeft: SCALE(25), marginRight: 0 }
109 | : { marginRight: SCALE(25), marginLeft: 0 };
110 |
111 | // Create container style with width and height
112 | const containerStyle = [
113 | styles.container,
114 | { backgroundColor: bgColor },
115 | width !== undefined && { width: width as DimensionValue },
116 | minHeight !== undefined && { minHeight: minHeight as DimensionValue },
117 | // Add shadow color based on theme
118 | {
119 | shadowColor: theme === 'dark' ? "#fff" : "#000",
120 | elevation: theme === 'dark' ? 8 : 5, // Slightly higher elevation for dark theme for better visibility
121 | },
122 | style
123 | ].filter(Boolean);
124 |
125 | // Render the icon based on type (string or ReactNode)
126 | const renderIcon = () => {
127 | // If icon is a ReactNode (custom component), render it directly
128 | if (React.isValidElement(icon)) {
129 | return (
130 |
131 | {icon}
132 |
133 | );
134 | }
135 |
136 | // If icon is a string, render the appropriate icon from the specified family
137 | if (typeof icon === 'string') {
138 | // Get the icon component for the specified family
139 | const IconComponent = IconFamilies[iconFamily as keyof typeof IconFamilies] || Icon;
140 |
141 | return (
142 |
149 | );
150 | }
151 |
152 | // Fallback to default icon if none provided
153 | return (
154 |
161 | );
162 | };
163 |
164 | return (
165 |
169 | {showCloseIcon && (
170 |
176 |
177 |
178 | )}
179 |
180 |
181 |
182 | {renderIcon()}
183 |
184 | {text1 ? (
185 |
190 | {text1}
191 |
192 | ) : null}
193 | {text2 ? (
194 |
199 | {text2}
200 |
201 | ) : null}
202 |
203 |
204 |
205 |
206 | {showProgressBar && (
207 |
211 | {isRTL ? (
212 | // For RTL: Start from left (0%) and grow to right (100%)
213 |
226 | ) : (
227 | // For LTR: Start from left (100%) and shrink to left (0%)
228 |
239 | )}
240 |
241 | )}
242 |
243 | );
244 | };
245 |
246 | const styles = StyleSheet.create({
247 | container: {
248 | width: '90%',
249 | minHeight: SCALE(61),
250 | borderRadius: 8,
251 | shadowOffset: {
252 | width: 0,
253 | height: 2,
254 | },
255 | shadowOpacity: 0.25,
256 | shadowRadius: 3.84,
257 | elevation: 5,
258 | alignItems: 'center',
259 | justifyContent: 'center',
260 | },
261 | hideButton: {
262 | position: "absolute",
263 | top: SCALE(3.2),
264 | right: SCALE(3.2),
265 | zIndex: 9999999,
266 | },
267 | content: {
268 | width: '100%',
269 | },
270 | contentInner: {
271 | paddingHorizontal: SCALE(12),
272 | flexDirection: "row",
273 | alignItems: "center",
274 | justifyContent: "flex-start",
275 | },
276 | iconWrapper: {
277 | marginRight: SCALE(8),
278 | },
279 | text1: {
280 | fontSize: SCALE(14),
281 | fontWeight: "500",
282 | },
283 | text2: {
284 | fontSize: SCALE(12),
285 | fontWeight: "400",
286 | marginTop: SCALE(4),
287 | opacity: 0.8,
288 | },
289 | progressBarContainer: {
290 | flexDirection: "row",
291 | position: "absolute",
292 | height: 4,
293 | width: "100%",
294 | bottom: 0,
295 | borderBottomLeftRadius: 8,
296 | borderBottomRightRadius: 8,
297 | overflow: 'hidden',
298 | },
299 | });
300 |
301 | export default BaseToast;
302 |
--------------------------------------------------------------------------------
/components/ErrorToast.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BaseToast from './BaseToast';
3 | import { Colors } from '../config/theme';
4 | import { ToastConfigParams } from '../utils/interfaces';
5 |
6 | const ErrorToast = ({
7 | text1,
8 | text2,
9 | hide,
10 | onPress,
11 | barWidth,
12 | isRTL,
13 | duration,
14 | showProgressBar,
15 | showCloseIcon,
16 | progressBarColor,
17 | backgroundColor,
18 | textColor,
19 | iconColor,
20 | iconSize,
21 | icon,
22 | iconFamily,
23 | width,
24 | minHeight,
25 | style,
26 | theme = 'light'
27 | }: ToastConfigParams) => {
28 | return (
29 |
52 | );
53 | };
54 |
55 | export default ErrorToast;
56 |
--------------------------------------------------------------------------------
/components/InfoToast.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BaseToast from './BaseToast';
3 | import { Colors } from '../config/theme';
4 | import { ToastConfigParams } from '../utils/interfaces';
5 |
6 | const InfoToast = ({
7 | text1,
8 | text2,
9 | hide,
10 | onPress,
11 | barWidth,
12 | isRTL,
13 | duration,
14 | showProgressBar,
15 | showCloseIcon,
16 | progressBarColor,
17 | backgroundColor,
18 | textColor,
19 | iconColor,
20 | iconSize,
21 | icon,
22 | iconFamily,
23 | width,
24 | minHeight,
25 | style,
26 | theme = 'light'
27 | }: ToastConfigParams) => {
28 | return (
29 |
52 | );
53 | };
54 |
55 | export default InfoToast;
56 |
--------------------------------------------------------------------------------
/components/SuccessToast.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BaseToast from './BaseToast';
3 | import { Colors } from '../config/theme';
4 | import { ToastConfigParams } from '../utils/interfaces';
5 |
6 | const SuccessToast = ({
7 | text1,
8 | text2,
9 | hide,
10 | onPress,
11 | barWidth,
12 | isRTL,
13 | duration,
14 | showProgressBar,
15 | showCloseIcon,
16 | progressBarColor,
17 | backgroundColor,
18 | textColor,
19 | iconColor,
20 | iconSize,
21 | icon,
22 | iconFamily,
23 | width,
24 | minHeight,
25 | style,
26 | theme = 'light'
27 | }: ToastConfigParams) => {
28 | return (
29 |
52 | );
53 | };
54 |
55 | export default SuccessToast;
56 |
--------------------------------------------------------------------------------
/components/ToastManager.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component, createRef, ReactNode, forwardRef, RefObject } from "react";
2 | import { Animated, Modal, TouchableOpacity, View } from "react-native";
3 |
4 | import {
5 | ToastManagerProps,
6 | ToastState,
7 | ToastRef,
8 | ToastPosition,
9 | ToastShowParams,
10 | ToastType
11 | } from "../utils/interfaces";
12 | import defaultProps from "../utils/defaultProps";
13 | import { Colors } from "../config/theme";
14 | import BaseToast from "./BaseToast";
15 | import styles from "./styles";
16 |
17 | class ToastManagerComponent extends Component {
18 | timerId: NodeJS.Timeout | null = null;
19 | animationRef: Animated.CompositeAnimation | null = null;
20 | static toastRef: RefObject = createRef();
21 | static defaultProps = defaultProps;
22 |
23 | constructor(props: ToastManagerProps) {
24 | super(props);
25 | this.state = {
26 | isVisible: false,
27 | type: 'default',
28 | text1: '',
29 | text2: '',
30 | position: props.position || 'top',
31 | duration: props.duration || 3000,
32 | barWidth: new Animated.Value(100),
33 | isPaused: false,
34 | useModal: props.useModal !== undefined ? props.useModal : true, // Default to true for backward compatibility
35 | };
36 | }
37 |
38 | getIconForType = (type: ToastType): string | ReactNode => {
39 | // First check if custom icons are provided in props
40 | if (this.props.icons && this.props.icons[type]) {
41 | return this.props.icons[type];
42 | }
43 |
44 | // Otherwise use default icon names
45 | switch (type) {
46 | case 'success': return 'checkmark-circle';
47 | case 'error': return 'alert-circle';
48 | case 'info': return 'information-circle';
49 | case 'warn': return 'warning';
50 | default: return 'checkmark-circle';
51 | }
52 | }
53 |
54 | getColorForType = (type: ToastType): string => {
55 | switch (type) {
56 | case 'success': return Colors.success;
57 | case 'error': return Colors.error;
58 | case 'info': return Colors.info;
59 | case 'warn': return Colors.warn;
60 | default: return Colors.default;
61 | }
62 | }
63 |
64 | // Map animation style to Modal's animationType
65 | getAnimationType = (): 'none' | 'slide' | 'fade' => {
66 | const { animationStyle } = this.props;
67 | return animationStyle || 'fade';
68 | }
69 |
70 | show = ({
71 | type = 'default',
72 | text1 = '',
73 | text2,
74 | position,
75 | visibilityTime,
76 | autoHide = true,
77 | props,
78 | onShow,
79 | onHide,
80 | onPress,
81 | progressBarColor,
82 | backgroundColor,
83 | textColor,
84 | iconColor,
85 | iconSize,
86 | icon,
87 | iconFamily,
88 | theme,
89 | useModal
90 | }: ToastShowParams): void => {
91 | // Clear any existing timers
92 | this.hide();
93 |
94 | // Reset animation if needed
95 | if (this.animationRef) {
96 | this.animationRef.stop();
97 | this.animationRef = null;
98 | }
99 |
100 | // Reset progress bar width
101 | this.state.barWidth.setValue(100);
102 |
103 | this.setState({
104 | isVisible: true,
105 | type: type || 'default',
106 | text1,
107 | text2,
108 | position: position || this.props.position || 'top',
109 | duration: visibilityTime || this.props.duration || 3000,
110 | props,
111 | isPaused: false,
112 | onShow,
113 | onHide,
114 | onPress,
115 | progressBarColor,
116 | backgroundColor,
117 | textColor,
118 | iconColor,
119 | iconSize,
120 | icon,
121 | iconFamily: iconFamily || this.props.iconFamily,
122 | theme: theme || this.props.theme,
123 | useModal: useModal !== undefined ? useModal : this.props.useModal
124 | }, () => {
125 | // Call onShow callback if provided
126 | if (this.state.onShow) {
127 | this.state.onShow();
128 | }
129 |
130 | // Start progress bar animation
131 | this.startProgressBarAnimation();
132 |
133 | // Set timer for auto-hide if enabled
134 | if (autoHide) {
135 | this.timerId = setTimeout(() => {
136 | this.hide();
137 | }, this.state.duration);
138 | }
139 | });
140 | }
141 |
142 | startProgressBarAnimation = (): void => {
143 | if (!this.props.showProgressBar) return;
144 |
145 | const { duration } = this.state;
146 | const { isRTL } = this.props;
147 |
148 | // Determine the start and end values based on RTL setting
149 | const startValue = isRTL ? 0 : 100;
150 | const endValue = isRTL ? 100 : 0;
151 |
152 | // Set initial value
153 | this.state.barWidth.setValue(startValue);
154 |
155 | this.animationRef = Animated.timing(this.state.barWidth, {
156 | toValue: endValue,
157 | duration,
158 | useNativeDriver: false
159 | });
160 |
161 | this.animationRef.start();
162 | }
163 |
164 | hide = (): void => {
165 | if (this.timerId) {
166 | clearTimeout(this.timerId);
167 | this.timerId = null;
168 | }
169 |
170 | if (this.animationRef) {
171 | this.animationRef.stop();
172 | this.animationRef = null;
173 | }
174 |
175 | if (this.state.isVisible) {
176 | this.setState({ isVisible: false }, () => {
177 | if (this.state.onHide) {
178 | this.state.onHide();
179 | }
180 | });
181 | }
182 | }
183 |
184 | pause = (): void => {
185 | if (this.timerId) {
186 | clearTimeout(this.timerId);
187 | this.timerId = null;
188 | }
189 |
190 | if (this.animationRef) {
191 | this.animationRef.stop();
192 | } else {
193 | Animated.timing(this.state.barWidth, {
194 | toValue: this.state.barWidth._value,
195 | duration: 1,
196 | useNativeDriver: false
197 | }).stop();
198 | }
199 |
200 | const currentValue = this.state.barWidth._value;
201 | const totalDuration = this.state.duration;
202 | const elapsedPercentage = this.props.isRTL
203 | ? currentValue / 100
204 | : 1 - (currentValue / 100);
205 |
206 | const remainingDuration = totalDuration * (1 - elapsedPercentage);
207 |
208 | this.setState({
209 | isPaused: true,
210 | pausedDuration: remainingDuration
211 | });
212 | }
213 |
214 | resume = (): void => {
215 | if (!this.state.isPaused) return;
216 |
217 | const remainingDuration = this.state.pausedDuration || 0;
218 |
219 | this.animationRef = Animated.timing(this.state.barWidth, {
220 | toValue: this.props.isRTL ? 100 : 0,
221 | duration: remainingDuration,
222 | useNativeDriver: false
223 | });
224 |
225 | this.animationRef.start();
226 |
227 | this.timerId = setTimeout(() => {
228 | this.hide();
229 | }, remainingDuration);
230 |
231 | this.setState({ isPaused: false, pausedDuration: undefined });
232 | }
233 |
234 | handlePress = (): void => {
235 | if (this.state.onPress) {
236 | this.state.onPress();
237 | }
238 | }
239 |
240 | getPositionStyle = (): object => {
241 | const { position } = this.state;
242 | const { topOffset, bottomOffset } = this.props;
243 |
244 | switch (position) {
245 | case 'top': return { top: topOffset || 40 };
246 | case 'bottom': return { bottom: bottomOffset || 40 };
247 | case 'center': return { top: 0, bottom: 0, justifyContent: 'center' };
248 | default: return { top: topOffset || 40 };
249 | }
250 | }
251 |
252 | renderToastContent = (): ReactNode => {
253 | const {
254 | config,
255 | theme = 'light',
256 | width,
257 | minHeight,
258 | style,
259 | textStyle,
260 | showCloseIcon,
261 | showProgressBar,
262 | isRTL,
263 | iconSize: propsIconSize,
264 | iconFamily: propsIconFamily
265 | } = this.props;
266 |
267 | const {
268 | type,
269 | text1,
270 | text2,
271 | props: customProps,
272 | barWidth,
273 | duration,
274 | position,
275 | progressBarColor,
276 | backgroundColor,
277 | textColor,
278 | iconColor,
279 | iconSize: stateIconSize,
280 | icon: stateIcon,
281 | iconFamily: stateIconFamily,
282 | theme: stateTheme
283 | } = this.state;
284 |
285 | // Use theme from state if provided, otherwise use theme from props
286 | const finalTheme = stateTheme || theme;
287 |
288 | // Use iconSize from state if provided, otherwise use from props
289 | const finalIconSize = stateIconSize !== undefined ? stateIconSize : propsIconSize;
290 |
291 | // Use icon from state if provided, otherwise get icon based on type
292 | const finalIcon = stateIcon !== undefined ? stateIcon : this.getIconForType(type);
293 |
294 | // Use iconFamily from state if provided, otherwise use from props
295 | const finalIconFamily = stateIconFamily || propsIconFamily || 'Ionicons';
296 |
297 | // Check if there's a custom component for this toast type
298 | if (config && typeof config[type] === 'function') {
299 | return config[type]({
300 | text1,
301 | text2,
302 | props: customProps,
303 | type,
304 | position,
305 | hide: this.hide,
306 | show: this.show,
307 | isVisible: this.state.isVisible,
308 | onPress: this.state.onPress,
309 | barWidth: barWidth,
310 | isRTL: isRTL,
311 | duration: duration,
312 | showProgressBar: showProgressBar,
313 | showCloseIcon: showCloseIcon,
314 | progressBarColor: progressBarColor,
315 | backgroundColor: backgroundColor,
316 | textColor: textColor,
317 | iconColor: iconColor,
318 | iconSize: finalIconSize,
319 | icon: finalIcon,
320 | iconFamily: finalIconFamily,
321 | width: width,
322 | minHeight: minHeight,
323 | style: style,
324 | theme: finalTheme,
325 | });
326 | }
327 |
328 | // Use default BaseToast component if no custom component is provided
329 | return (
330 |
353 | );
354 | }
355 |
356 | // Render the toast content with a TouchableOpacity wrapper
357 | renderToastWithTouchable = () => {
358 | const { position } = this.state;
359 |
360 | return (
361 | this.pause()}
364 | onPressOut={() => this.resume()}
365 | style={[
366 | styles.containerRoot,
367 | position === 'top' ? styles.containerTop :
368 | position === 'bottom' ? styles.containerBottom : {},
369 | this.getPositionStyle(),
370 | styles.toastContainer, // Add high zIndex
371 | ]}
372 | testID="toast-container"
373 | >
374 | {this.renderToastContent()}
375 |
376 | );
377 | }
378 |
379 | render() {
380 | const { isVisible, position, useModal } = this.state;
381 |
382 | // If not using Modal, render directly in the component tree with high zIndex
383 | if (!useModal) {
384 | return isVisible ? this.renderToastWithTouchable() : null;
385 | }
386 |
387 | // Otherwise use Modal for backward compatibility
388 | return (
389 |
390 |
397 | this.pause()}
400 | onPressOut={() => this.resume()}
401 | style={[
402 | styles.containerRoot,
403 | position === 'top' ? styles.containerTop :
404 | position === 'bottom' ? styles.containerBottom : {},
405 | this.getPositionStyle(),
406 | ]}
407 | testID="toast-container"
408 | >
409 | {this.renderToastContent()}
410 |
411 |
412 |
413 | );
414 | }
415 | }
416 |
417 | const ToastManager = forwardRef((props: ToastManagerProps, ref) => {
418 | return ;
419 | });
420 |
421 | // Define the type for the ToastManager component with static methods
422 | interface ToastManagerType extends React.ForwardRefExoticComponent<
423 | ToastManagerProps & React.RefAttributes
424 | > {
425 | setRef: (ref: any) => void;
426 | getRef: () => RefObject;
427 | show: (options: ToastShowParams) => void;
428 | hide: () => void;
429 | success: (text: string, position?: ToastPosition) => void;
430 | error: (text: string, position?: ToastPosition) => void;
431 | info: (text: string, position?: ToastPosition) => void;
432 | warn: (text: string, position?: ToastPosition) => void;
433 | defaultProps: ToastManagerProps;
434 | }
435 |
436 | (ToastManager as ToastManagerType).setRef = (ref: any) => {
437 | ToastManagerComponent.toastRef = ref;
438 | };
439 |
440 | (ToastManager as ToastManagerType).getRef = () => {
441 | return ToastManagerComponent.toastRef;
442 | };
443 |
444 | (ToastManager as ToastManagerType).show = (options: ToastShowParams) => {
445 | ToastManagerComponent.toastRef?.current?.show(options);
446 | };
447 |
448 | (ToastManager as ToastManagerType).hide = () => {
449 | ToastManagerComponent.toastRef?.current?.hide();
450 | };
451 |
452 | (ToastManager as ToastManagerType).success = (text: string, position?: ToastPosition) => {
453 | ToastManagerComponent.toastRef?.current?.show({
454 | type: 'success',
455 | text1: text,
456 | position
457 | });
458 | };
459 |
460 | (ToastManager as ToastManagerType).error = (text: string, position?: ToastPosition) => {
461 | ToastManagerComponent.toastRef?.current?.show({
462 | type: 'error',
463 | text1: text,
464 | position
465 | });
466 | };
467 |
468 | (ToastManager as ToastManagerType).info = (text: string, position?: ToastPosition) => {
469 | ToastManagerComponent.toastRef?.current?.show({
470 | type: 'info',
471 | text1: text,
472 | position
473 | });
474 | };
475 |
476 | (ToastManager as ToastManagerType).warn = (text: string, position?: ToastPosition) => {
477 | ToastManagerComponent.toastRef?.current?.show({
478 | type: 'warn',
479 | text1: text,
480 | position
481 | });
482 | };
483 |
484 | // Copy defaultProps to the forwarded ref component
485 | (ToastManager as ToastManagerType).defaultProps = ToastManagerComponent.defaultProps;
486 |
487 | export default ToastManager as ToastManagerType;
488 |
--------------------------------------------------------------------------------
/components/WarnToast.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BaseToast from './BaseToast';
3 | import { Colors } from '../config/theme';
4 | import { ToastConfigParams } from '../utils/interfaces';
5 |
6 | const WarnToast = ({
7 | text1,
8 | text2,
9 | hide,
10 | onPress,
11 | barWidth,
12 | isRTL,
13 | duration,
14 | showProgressBar,
15 | showCloseIcon,
16 | progressBarColor,
17 | backgroundColor,
18 | textColor,
19 | iconColor,
20 | iconSize,
21 | icon,
22 | iconFamily,
23 | width,
24 | minHeight,
25 | style,
26 | theme = 'light'
27 | }: ToastConfigParams) => {
28 | return (
29 |
52 | );
53 | };
54 |
55 | export default WarnToast;
56 |
--------------------------------------------------------------------------------
/components/styles.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native'
2 |
3 | import { SCALE } from '../utils/helpers'
4 |
5 | const styles = StyleSheet.create({
6 | containerRoot: {
7 | position: 'absolute',
8 | left: 0,
9 | right: 0,
10 | zIndex: 9999999,
11 | justifyContent: 'center',
12 | alignItems: 'center',
13 | },
14 | containerTop: {
15 | top: 0,
16 | },
17 | containerBottom: {
18 | bottom: 0,
19 | },
20 | mainContainer: {
21 | borderRadius: 8,
22 | flexDirection: 'column',
23 | alignItems: 'flex-start',
24 | justifyContent: 'center',
25 | shadowColor: '#000',
26 | shadowOffset: {
27 | width: 0,
28 | height: 2,
29 | },
30 | shadowOpacity: 0.25,
31 | shadowRadius: 3.84,
32 | elevation: 5,
33 | },
34 | hideButton: {
35 | position: 'absolute',
36 | top: SCALE(3.2),
37 | right: SCALE(3.2),
38 | },
39 | textStyle: {
40 | fontSize: SCALE(16),
41 | fontWeight: '400',
42 | flex: 1,
43 | width: 'auto',
44 | maxWidth: '85%',
45 | },
46 | progressBarContainer: {
47 | flexDirection: 'row',
48 | position: 'absolute',
49 | height: 4,
50 | width: '100%',
51 | bottom: 0,
52 | borderBottomLeftRadius: 8,
53 | borderBottomRightRadius: 8,
54 | overflow: 'hidden',
55 | },
56 | content: {
57 | width: '100%',
58 | flexDirection: 'row',
59 | alignItems: 'center',
60 | justifyContent: 'flex-start',
61 | },
62 | iconWrapper: {
63 | marginRight: SCALE(8),
64 | },
65 | pressable: {
66 | width: '100%',
67 | },
68 | toastContainer: {
69 | position: 'absolute',
70 | zIndex: 9999, // Very high zIndex to ensure it's on top of other elements
71 | elevation: 9999, // For Android
72 | left: 0,
73 | right: 0,
74 | alignItems: 'center',
75 | },
76 | })
77 |
78 | export default styles
79 |
--------------------------------------------------------------------------------
/config/theme.ts:
--------------------------------------------------------------------------------
1 | export const Colors: any = {
2 | light: {
3 | text: '#000',
4 | back: '#ffffff',
5 | },
6 | dark: {
7 | text: '#ffffff',
8 | back: '#2B2D2E',
9 | },
10 | default: '#3498db',
11 | info: '#3498db',
12 | success: '#07bc0c',
13 | warn: '#f1c40f',
14 | error: '#e74c3c',
15 | textDefault: '#4c4c4c',
16 | textDark: 'black',
17 | }
18 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | import React, { createRef, ReactNode } from 'react'
2 | import ToastManager from './components/ToastManager'
3 | import { ToastRef, ToastShowParams, ToastPosition, IconFamily } from './utils/interfaces'
4 | import defaultConfig from './utils/defaultConfig'
5 |
6 | // Create a ref to the ToastManager
7 | const toastRef = createRef()
8 |
9 | // Set the ref to the ToastManager
10 | ToastManager.setRef(toastRef)
11 |
12 | // Export toast functions that use the ref
13 | export const Toast = {
14 | show: (options: ToastShowParams) => {
15 | toastRef.current?.show(options)
16 | },
17 | hide: () => {
18 | toastRef.current?.hide()
19 | },
20 | success: (
21 | text: string,
22 | position?: ToastPosition,
23 | icon?: string | ReactNode,
24 | iconFamily?: IconFamily,
25 | useModal?: boolean,
26 | ) => {
27 | toastRef.current?.show({
28 | type: 'success',
29 | text1: text,
30 | position,
31 | icon,
32 | iconFamily,
33 | useModal,
34 | })
35 | },
36 | error: (
37 | text: string,
38 | position?: ToastPosition,
39 | icon?: string | ReactNode,
40 | iconFamily?: IconFamily,
41 | useModal?: boolean,
42 | ) => {
43 | toastRef.current?.show({
44 | type: 'error',
45 | text1: text,
46 | position,
47 | icon,
48 | iconFamily,
49 | useModal,
50 | })
51 | },
52 | info: (
53 | text: string,
54 | position?: ToastPosition,
55 | icon?: string | ReactNode,
56 | iconFamily?: IconFamily,
57 | useModal?: boolean,
58 | ) => {
59 | toastRef.current?.show({
60 | type: 'info',
61 | text1: text,
62 | position,
63 | icon,
64 | iconFamily,
65 | useModal,
66 | })
67 | },
68 | warn: (
69 | text: string,
70 | position?: ToastPosition,
71 | icon?: string | ReactNode,
72 | iconFamily?: IconFamily,
73 | useModal?: boolean,
74 | ) => {
75 | toastRef.current?.show({
76 | type: 'warn',
77 | text1: text,
78 | position,
79 | icon,
80 | iconFamily,
81 | useModal,
82 | })
83 | },
84 | }
85 |
86 | // Add a special JSX component that should be placed at the root level of your app
87 | export default function ToastProvider(props: any) {
88 | // Merge provided config with default config
89 | const mergedConfig = props.config ? { ...defaultConfig, ...props.config } : defaultConfig
90 |
91 | return React.createElement(ToastManager, {
92 | ...props,
93 | config: mergedConfig,
94 | ref: toastRef,
95 | })
96 | }
97 |
98 | // Export the components for custom configuration
99 | export { default as BaseToast } from './components/BaseToast'
100 | export { default as SuccessToast } from './components/SuccessToast'
101 | export { default as ErrorToast } from './components/ErrorToast'
102 | export { default as InfoToast } from './components/InfoToast'
103 | export { default as WarnToast } from './components/WarnToast'
104 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "toastify-react-native",
3 | "version": "7.2.0",
4 | "description": "🎉 toastify-react-native allows you to add notifications to your react-native app (ios, android) with ease. No more nonsense!",
5 | "main": "index.ts",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "typecheck": "tsc --noEmit"
9 | },
10 | "keywords": [
11 | "react",
12 | "react-component",
13 | "pushalert",
14 | "toast",
15 | "popup",
16 | "react-native toast",
17 | "react-native-toastify",
18 | "react-native",
19 | "react-toastify",
20 | "toastify",
21 | "react-native-notification",
22 | "toastify-react-native",
23 | "notification",
24 | "picker",
25 | "expo",
26 | "cli",
27 | "mobile",
28 | "ios",
29 | "android",
30 | "web",
31 | "Typescript"
32 | ],
33 | "bugs": {
34 | "url": "https://github.com/zahidalidev/toastify-react-native/issues"
35 | },
36 | "homepage": "https://github.com/zahidalidev/toastify-react-native/blob/master/README.md",
37 | "peerDependencies": {
38 | "react": "*",
39 | "react-native": "*"
40 | },
41 | "devDependencies": {
42 | "@babel/core": "^7.15.8",
43 | "@types/react-native-vector-icons": "^6.4.18",
44 | "metro-react-native-babel-preset": "^0.71.0",
45 | "prettier": "^3.3.3",
46 | "typescript": "^5.8.2"
47 | },
48 | "repository": "git://github.com/zahidalidev/toastify-react-native.git",
49 | "author": "Zahid Ali ",
50 | "license": "MIT",
51 | "dependencies": {
52 | "react-native-vector-icons": "*"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # Expo
7 | .expo/
8 | dist/
9 | web-build/
10 | expo-env.d.ts
11 |
12 | # Native
13 | *.orig.*
14 | *.jks
15 | *.p8
16 | *.p12
17 | *.key
18 | *.mobileprovision
19 |
20 | # Metro
21 | .metro-health-check*
22 |
23 | # debug
24 | npm-debug.*
25 | yarn-debug.*
26 | yarn-error.*
27 |
28 | # macOS
29 | .DS_Store
30 | *.pem
31 |
32 | # local env files
33 | .env*.local
34 |
35 | # typescript
36 | *.tsbuildinfo
37 |
--------------------------------------------------------------------------------
/sample/Another.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { StyleSheet, View, TouchableOpacity, Text } from "react-native";
3 | import { Toast } from "toastify-react-native";
4 |
5 | const Another = () => (
6 |
7 | Toast.info("Lorem ipsum info", "bottom")} style={styles.buttonStyle}>
8 | SHOW SOME AWESOMENESS!
9 |
10 |
11 | );
12 |
13 | const styles = StyleSheet.create({
14 | container: {
15 | backgroundColor: "#fff",
16 | alignItems: "center",
17 | justifyContent: "center",
18 | },
19 | buttonStyle: {
20 | marginTop: 10,
21 | backgroundColor: "white",
22 | borderColor: "green",
23 | borderWidth: 2,
24 | padding: 10,
25 | },
26 | });
27 |
28 | export default Another;
29 |
--------------------------------------------------------------------------------
/sample/App.js:
--------------------------------------------------------------------------------
1 | // App.js
2 | import React, { useState } from 'react'
3 | import {
4 | View,
5 | Button,
6 | Text,
7 | StyleSheet,
8 | ScrollView,
9 | Switch,
10 | SafeAreaView,
11 | Platform,
12 | StatusBar,
13 | } from 'react-native'
14 | import ToastManager, { Toast } from 'toastify-react-native'
15 | import Icon from 'react-native-vector-icons/MaterialIcons'
16 | import FontAwesome from 'react-native-vector-icons/FontAwesome'
17 |
18 | // Custom icon component example
19 | const CustomIconComponent = ({ color }) => (
20 |
30 |
31 |
32 | )
33 |
34 | // Custom toast component - simplified without progress bar
35 | const CustomToast = ({ text1, text2, hide, iconColor }) => (
36 |
37 |
38 |
39 | {text1}
40 | {text2 && {text2}}
41 |
42 |
43 |
44 | )
45 |
46 | // Custom toast configuration
47 | const toastConfig = {
48 | customSuccess: (props) => (
49 |
50 |
51 |
52 | {props.text1}
53 | {props.text2 && {props.text2}}
54 |
55 |
56 | ),
57 | custom: (props) => ,
58 | }
59 |
60 | export default function App() {
61 | const [isDarkMode, setIsDarkMode] = useState(false)
62 | const [isRTL, setIsRTL] = useState(false)
63 | const [position, setPosition] = useState('bottom')
64 | const [showProgressBar, setShowProgressBar] = useState(true)
65 | const [showCloseIcon, setShowCloseIcon] = useState(true)
66 | const [useModal, setUseModal] = useState(false)
67 |
68 | // Toggle between positions
69 | const togglePosition = () => {
70 | if (position === 'top') setPosition('center')
71 | else if (position === 'center') setPosition('bottom')
72 | else setPosition('top')
73 | }
74 |
75 | return (
76 |
77 |
78 |
79 | Toastify React Native Demo
80 | Version 7.0.0
81 |
82 |
83 |
84 | Dark Mode
85 |
86 |
87 |
88 |
89 | RTL Support
90 |
91 |
92 |
93 |
94 | Show Progress Bar
95 |
96 |
97 |
98 |
99 | Show Close Icon
100 |
101 |
102 |
103 |
104 | Use Modal
105 |
106 |
107 |
108 |
109 | Position: {position}
110 |
111 |
112 |
113 |
114 | Basic Toasts
115 |
116 | {
119 | Toast.success('Success message!', position)
120 | }}
121 | />
122 |
123 | {
126 | Toast.error('Error message!', position)
127 | }}
128 | />
129 |
130 |
131 |
132 | {
135 | Toast.info('Info message!', position)
136 | }}
137 | />
138 |
139 | {
142 | Toast.warn('Warning message!', position)
143 | }}
144 | />
145 |
146 |
147 | Advanced Features
148 |
157 | {
160 | Toast.show({
161 | type: 'success',
162 | text1: 'Main message',
163 | text2: 'This is a secondary message',
164 | position,
165 | })
166 | }}
167 | />
168 |
169 | {
172 | Toast.show({
173 | type: 'error',
174 | text1: 'Custom Colors',
175 | text2: 'With custom background and progress bar',
176 | position,
177 | backgroundColor: '#673AB7',
178 | textColor: '#FFFFFF',
179 | progressBarColor: '#FF9800',
180 | })
181 | }}
182 | />
183 |
184 | {
187 | Toast.show({
188 | type: 'info',
189 | text1: 'Larger Icon',
190 | text2: 'This toast has a larger icon',
191 | position,
192 | iconSize: 30,
193 | iconColor: '#FF9800',
194 | })
195 | }}
196 | />
197 |
198 | {
201 | Toast.show({
202 | type: 'success',
203 | text1: 'Long Duration',
204 | text2: 'This toast will stay for 8 seconds',
205 | position,
206 | visibilityTime: 8000,
207 | })
208 | }}
209 | />
210 |
211 | {
214 | Toast.show({
215 | type: 'warn',
216 | text1: 'No Auto Hide',
217 | text2: 'This toast will not hide automatically',
218 | position,
219 | autoHide: false,
220 | })
221 | }}
222 | />
223 |
224 | {
227 | Toast.show({
228 | type: 'success',
229 | text1: 'With Callbacks',
230 | text2: 'Check console for logs',
231 | position,
232 | onPress: () => console.log('Toast pressed'),
233 | onShow: () => console.log('Toast shown'),
234 | onHide: () => console.log('Toast hidden'),
235 | })
236 | }}
237 | />
238 |
239 |
240 |
241 | Modal Options - For Interactive and Non-Interactive Background
242 |
243 |
252 | {
255 | Toast.show({
256 | type: 'success',
257 | text1: 'Using Modal',
258 | text2: 'Background is not interactive',
259 | position,
260 | useModal: true,
261 | })
262 | }}
263 | />
264 |
265 | {
268 | Toast.show({
269 | type: 'error',
270 | text1: 'No Modal',
271 | text2: 'Background remains interactive',
272 | position,
273 | useModal: false,
274 | })
275 | }}
276 | />
277 |
278 | {
281 | // Using the shorthand success method with useModal parameter
282 | Toast.success(
283 | 'Success with Modal!',
284 | position,
285 | undefined, // icon
286 | undefined, // iconFamily
287 | true, // useModal
288 | )
289 | }}
290 | />
291 |
292 | {
295 | // Using the shorthand error method with useModal parameter
296 | Toast.error(
297 | 'Error without Modal!',
298 | position,
299 | undefined, // icon
300 | undefined, // iconFamily
301 | false, // useModal
302 | )
303 | }}
304 | />
305 |
306 |
307 | Custom Icons
308 |
317 | {/* Different icon name from same family */}
318 | {
321 | Toast.show({
322 | type: 'success',
323 | text1: 'Different Icon',
324 | text2: 'Using a different icon name',
325 | position,
326 | icon: 'checkmark-circle-outline',
327 | })
328 | }}
329 | />
330 |
331 | {/* Different icon family */}
332 | {
335 | Toast.show({
336 | type: 'error',
337 | text1: 'FontAwesome Icon',
338 | text2: 'Using a different icon family',
339 | position,
340 | icon: 'exclamation-circle',
341 | iconFamily: 'FontAwesome',
342 | })
343 | }}
344 | />
345 |
346 | {/* Custom icon component */}
347 | {
350 | Toast.show({
351 | type: 'info',
352 | text1: 'Custom Component',
353 | text2: 'Using a custom React component as icon',
354 | position,
355 | icon: ,
356 | })
357 | }}
358 | />
359 |
360 | {/* Ionicons example */}
361 | {
364 | Toast.show({
365 | type: 'warn',
366 | text1: 'Ionicons Example',
367 | text2: 'Using Ionicons family explicitly',
368 | position,
369 | icon: 'alert',
370 | iconFamily: 'Ionicons',
371 | iconColor: '#FFC107',
372 | })
373 | }}
374 | />
375 |
376 | {/* Using JSX directly as icon */}
377 | {
380 | Toast.show({
381 | type: 'success',
382 | text1: 'JSX Icon',
383 | text2: 'Using JSX directly as icon',
384 | position,
385 | icon: (
386 |
387 |
388 |
394 |
395 | ),
396 | })
397 | }}
398 | />
399 |
400 |
401 | Custom Components
402 |
411 | {
414 | Toast.show({
415 | type: 'customSuccess',
416 | text1: 'Custom Success',
417 | text2: 'Using the custom success component',
418 | position,
419 | })
420 | }}
421 | />
422 |
423 | {
426 | Toast.show({
427 | type: 'custom',
428 | text1: 'Fully Custom',
429 | text2: 'This is a completely custom component',
430 | position,
431 | iconColor: '#FF5722',
432 | progressBarColor: '#4CAF50',
433 | })
434 | }}
435 | />
436 |
437 |
438 |
439 |
440 | {/* Toast provider with all the configured options */}
441 |
451 |
452 | )
453 | }
454 |
455 | const styles = StyleSheet.create({
456 | safeArea: {
457 | flex: 1,
458 | paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0,
459 | backgroundColor: '#f5f5f5',
460 | },
461 | scrollContainer: {
462 | flexGrow: 1,
463 | },
464 | container: {
465 | flex: 1,
466 | padding: 20,
467 | alignItems: 'center',
468 | justifyContent: 'center',
469 | },
470 | title: {
471 | fontSize: 24,
472 | fontWeight: 'bold',
473 | marginBottom: 5,
474 | textAlign: 'center',
475 | },
476 | subtitle: {
477 | fontSize: 16,
478 | marginBottom: 20,
479 | color: '#666',
480 | textAlign: 'center',
481 | },
482 | settingsContainer: {
483 | width: '100%',
484 | backgroundColor: '#fff',
485 | borderRadius: 10,
486 | padding: 15,
487 | marginBottom: 20,
488 | shadowColor: '#000',
489 | shadowOffset: { width: 0, height: 2 },
490 | shadowOpacity: 0.1,
491 | shadowRadius: 3,
492 | elevation: 3,
493 | },
494 | settingRow: {
495 | flexDirection: 'row',
496 | justifyContent: 'space-between',
497 | alignItems: 'center',
498 | paddingVertical: 8,
499 | },
500 | sectionTitle: {
501 | fontSize: 18,
502 | fontWeight: 'bold',
503 | marginTop: 15,
504 | marginBottom: 10,
505 | alignSelf: 'flex-start',
506 | },
507 | buttonRow: {
508 | flexDirection: 'row',
509 | justifyContent: 'space-between',
510 | width: '100%',
511 | marginBottom: 10,
512 | },
513 | customSuccessToast: {
514 | width: '90%',
515 | backgroundColor: '#4CAF50',
516 | borderRadius: 10,
517 | padding: 15,
518 | flexDirection: 'row',
519 | alignItems: 'center',
520 | shadowColor: '#000',
521 | shadowOffset: { width: 0, height: 2 },
522 | shadowOpacity: 0.25,
523 | shadowRadius: 3.84,
524 | elevation: 5,
525 | },
526 | customToast: {
527 | width: '90%',
528 | backgroundColor: '#673AB7',
529 | borderRadius: 10,
530 | padding: 15,
531 | flexDirection: 'row',
532 | alignItems: 'center',
533 | shadowColor: '#000',
534 | shadowOffset: { width: 0, height: 2 },
535 | shadowOpacity: 0.25,
536 | shadowRadius: 3.84,
537 | elevation: 5,
538 | },
539 | textContainer: {
540 | flex: 1,
541 | marginLeft: 10,
542 | },
543 | customTitle: {
544 | color: '#fff',
545 | fontWeight: 'bold',
546 | fontSize: 16,
547 | },
548 | customMessage: {
549 | color: '#fff',
550 | fontSize: 14,
551 | marginTop: 4,
552 | },
553 | customProgressContainer: {
554 | position: 'absolute',
555 | bottom: 0,
556 | left: 0,
557 | right: 0,
558 | height: 4,
559 | backgroundColor: 'rgba(255,255,255,0.2)',
560 | borderBottomLeftRadius: 10,
561 | borderBottomRightRadius: 10,
562 | overflow: 'hidden',
563 | },
564 | })
565 |
--------------------------------------------------------------------------------
/sample/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "my-app",
4 | "slug": "my-app",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "userInterfaceStyle": "light",
9 | "newArchEnabled": true,
10 | "splash": {
11 | "image": "./assets/splash-icon.png",
12 | "resizeMode": "contain",
13 | "backgroundColor": "#ffffff"
14 | },
15 | "ios": {
16 | "supportsTablet": true
17 | },
18 | "android": {
19 | "adaptiveIcon": {
20 | "foregroundImage": "./assets/adaptive-icon.png",
21 | "backgroundColor": "#ffffff"
22 | }
23 | },
24 | "web": {
25 | "favicon": "./assets/favicon.png"
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/sample/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zahidalidev/toastify-react-native/2410fc40480a56b312f05f27652e61db7fa0cf26/sample/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/sample/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zahidalidev/toastify-react-native/2410fc40480a56b312f05f27652e61db7fa0cf26/sample/assets/favicon.png
--------------------------------------------------------------------------------
/sample/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zahidalidev/toastify-react-native/2410fc40480a56b312f05f27652e61db7fa0cf26/sample/assets/icon.png
--------------------------------------------------------------------------------
/sample/assets/splash-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zahidalidev/toastify-react-native/2410fc40480a56b312f05f27652e61db7fa0cf26/sample/assets/splash-icon.png
--------------------------------------------------------------------------------
/sample/index.js:
--------------------------------------------------------------------------------
1 | import { registerRootComponent } from 'expo'
2 |
3 | import App from './App'
4 |
5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6 | // It also ensures that whether you load the app in Expo Go or in a native build,
7 | // the environment is set up appropriately
8 | registerRootComponent(App)
9 |
--------------------------------------------------------------------------------
/sample/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig } = require('expo/metro-config');
2 | const path = require('path');
3 |
4 | const projectRoot = __dirname;
5 | const workspaceRoot = path.resolve(projectRoot, '..');
6 |
7 | const config = getDefaultConfig(projectRoot);
8 |
9 | config.watchFolders = [workspaceRoot];
10 | config.resolver = {
11 | ...config.resolver,
12 | nodeModulesPaths: [
13 | path.resolve(projectRoot, 'node_modules'),
14 | path.resolve(workspaceRoot, 'node_modules'),
15 | ],
16 | disableHierarchicalLookup: true,
17 | extraNodeModules: new Proxy({}, {
18 | get: (target, name) => path.join(projectRoot, `node_modules/${name}`),
19 | }),
20 | };
21 |
22 | module.exports = config;
23 |
--------------------------------------------------------------------------------
/sample/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-app",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "web": "expo start --web"
10 | },
11 | "dependencies": {
12 | "expo": "*",
13 | "expo-status-bar": "*",
14 | "react": "*",
15 | "react-native": "*",
16 | "react-native-animatable": "*",
17 | "react-native-vector-icons": "^10.2.0",
18 | "toastify-react-native": "file:.."
19 | },
20 | "devDependencies": {
21 | "@babel/core": "^7.20.0",
22 | "typescript": "^5.3.3",
23 | "@types/react": "~18.3.12"
24 | },
25 | "private": true
26 | }
27 |
--------------------------------------------------------------------------------
/sample/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {},
3 | "extends": "expo/tsconfig.base"
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "module": "commonjs",
5 | "lib": ["es2017"],
6 | "jsx": "react-native",
7 | "declaration": true,
8 | "sourceMap": true,
9 | "outDir": "./lib",
10 | "rootDir": "./",
11 | "strict": true,
12 | "moduleResolution": "node",
13 | "baseUrl": "./",
14 | "esModuleInterop": true,
15 | "skipLibCheck": true,
16 | "forceConsistentCasingInFileNames": true
17 | },
18 | "include": [
19 | "**/*.ts",
20 | "**/*.tsx"
21 | ],
22 | "exclude": [
23 | "node_modules",
24 | "sample",
25 | "lib"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/utils/defaultConfig.ts:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { ToastConfig, ToastConfigParams } from './interfaces'
4 | import SuccessToast from '../components/SuccessToast'
5 | import ErrorToast from '../components/ErrorToast'
6 | import InfoToast from '../components/InfoToast'
7 | import WarnToast from '../components/WarnToast'
8 |
9 | const defaultConfig: ToastConfig = {
10 | success: (props: ToastConfigParams) => React.createElement(SuccessToast, props),
11 | error: (props: ToastConfigParams) => React.createElement(ErrorToast, props),
12 | info: (props: ToastConfigParams) => React.createElement(InfoToast, props),
13 | warn: (props: ToastConfigParams) => React.createElement(WarnToast, props),
14 | default: (props: ToastConfigParams) => React.createElement(InfoToast, props),
15 | }
16 |
17 | export default defaultConfig
18 |
--------------------------------------------------------------------------------
/utils/defaultProps.ts:
--------------------------------------------------------------------------------
1 | import { ToastManagerProps } from './interfaces'
2 | import { SCALE } from './helpers'
3 |
4 | const defaultProps: ToastManagerProps = {
5 | theme: 'light',
6 | width: '90%',
7 | minHeight: SCALE(61),
8 | style: {},
9 | textStyle: {},
10 | position: 'top',
11 | duration: 3000,
12 | animationStyle: 'fade',
13 | topOffset: 40,
14 | bottomOffset: 40,
15 | showCloseIcon: true,
16 | showProgressBar: true,
17 | isRTL: false,
18 | iconSize: SCALE(22),
19 | iconFamily: 'Ionicons',
20 | icons: {
21 | success: 'checkmark-circle',
22 | error: 'alert-circle',
23 | info: 'information-circle',
24 | warn: 'warning',
25 | default: 'checkmark-circle',
26 | },
27 | useModal: true,
28 | }
29 |
30 | export default defaultProps
31 |
--------------------------------------------------------------------------------
/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | import { Platform, Dimensions, PixelRatio } from 'react-native'
2 |
3 | const { width } = Dimensions.get('window')
4 |
5 | export const isAndroid: boolean = Platform.OS === 'android'
6 | export const isIOS: boolean = Platform.OS === 'ios'
7 |
8 | export const SCALE = (size: number, androidRatio: number = 1, iOSRatio: number = 1): number => {
9 | const baseWidth: number = 375
10 | const scaleFactor: number = Math.min(width / baseWidth, 1.2)
11 | const platformRatio: number = isAndroid ? androidRatio : iOSRatio
12 | const pixelDensity: number = PixelRatio.get()
13 |
14 | const densityAdjustment: number = 3 / pixelDensity
15 |
16 | const newSize: number = size * scaleFactor * platformRatio * densityAdjustment
17 |
18 | const minSize: number = size * (isAndroid ? androidRatio : 0.8)
19 | const maxSize: number = size * 1.3
20 |
21 | return Math.min(Math.max(newSize, minSize), maxSize)
22 | }
23 |
24 | // Helper to handle different toast positions
25 | export const getToastPositionStyle = (
26 | position: string,
27 | topOffset: number = 40,
28 | bottomOffset: number = 40,
29 | ) => {
30 | switch (position) {
31 | case 'top':
32 | return { top: topOffset }
33 | case 'bottom':
34 | return { bottom: bottomOffset }
35 | case 'center':
36 | return { top: 0, bottom: 0, justifyContent: 'center' }
37 | default:
38 | return { top: topOffset }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/utils/interfaces.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 | import { StyleProp, TextStyle, ViewStyle } from 'react-native'
3 |
4 | export type ToastPosition = 'top' | 'center' | 'bottom'
5 | export type ToastType = 'success' | 'error' | 'info' | 'warn' | 'default'
6 | export type AnimationStyle = 'none' | 'slide' | 'fade'
7 | export type IconFamily =
8 | | 'Ionicons'
9 | | 'MaterialIcons'
10 | | 'FontAwesome'
11 | | 'FontAwesome5'
12 | | 'MaterialCommunityIcons'
13 | | 'Entypo'
14 | | 'Feather'
15 | | 'AntDesign'
16 | | 'Octicons'
17 | | 'SimpleLineIcons'
18 |
19 | export interface ToastShowParams {
20 | type?: ToastType
21 | text1?: string
22 | text2?: string
23 | position?: ToastPosition
24 | visibilityTime?: number
25 | autoHide?: boolean
26 | topOffset?: number
27 | bottomOffset?: number
28 | props?: Record
29 | onShow?: () => void
30 | onHide?: () => void
31 | onPress?: () => void
32 | progressBarColor?: string
33 | backgroundColor?: string
34 | textColor?: string
35 | iconColor?: string
36 | iconSize?: number
37 | icon?: string | ReactNode
38 | iconFamily?: IconFamily
39 | theme?: 'light' | 'dark'
40 | testID?: string
41 | useModal?: boolean
42 | }
43 |
44 | export interface ToastConfig {
45 | [key: string]: (props: ToastConfigParams) => ReactNode
46 | }
47 |
48 | export interface ToastConfigParams {
49 | text1?: string
50 | text2?: string
51 | type?: ToastType
52 | props?: any
53 | position?: ToastPosition
54 | hide?: () => void
55 | show?: (options: ToastShowParams) => void
56 | isVisible?: boolean
57 | onPress?: () => void
58 | barWidth?: any
59 | isRTL?: boolean
60 | duration?: number
61 | showProgressBar?: boolean
62 | showCloseIcon?: boolean
63 | progressBarColor?: string
64 | backgroundColor?: string
65 | textColor?: string
66 | iconColor?: string
67 | iconSize?: number
68 | icon?: string | ReactNode
69 | iconFamily?: IconFamily
70 | testID?: string
71 | width?: number | string
72 | minHeight?: number | string
73 | style?: StyleProp
74 | theme?: 'light' | 'dark'
75 | useModal?: boolean
76 | }
77 |
78 | export interface ToastManagerProps {
79 | width?: number | string | 'auto'
80 | minHeight?: number | string | 'auto'
81 | duration?: number
82 | style?: StyleProp
83 | textStyle?: StyleProp
84 | theme?: 'light' | 'dark'
85 | animationStyle?: AnimationStyle
86 | position?: ToastPosition
87 | showCloseIcon?: boolean
88 | showProgressBar?: boolean
89 | isRTL?: boolean
90 | config?: ToastConfig
91 | ref?: any
92 | topOffset?: number
93 | bottomOffset?: number
94 | testID?: string
95 | iconSize?: number
96 | icons?: {
97 | success?: string | ReactNode
98 | error?: string | ReactNode
99 | info?: string | ReactNode
100 | warn?: string | ReactNode
101 | default?: string | ReactNode
102 | }
103 | iconFamily?: IconFamily
104 | useModal?: boolean
105 | }
106 |
107 | export interface ToastRef {
108 | show: (options: ToastShowParams) => void
109 | hide: () => void
110 | success: (text: string, position?: ToastPosition) => void
111 | error: (text: string, position?: ToastPosition) => void
112 | info: (text: string, position?: ToastPosition) => void
113 | warn: (text: string, position?: ToastPosition) => void
114 | }
115 |
116 | export interface ToastState {
117 | isVisible: boolean
118 | type: ToastType
119 | text1: string
120 | text2?: string
121 | position: ToastPosition
122 | props?: Record
123 | duration: number
124 | barWidth: any
125 | isPaused: boolean
126 | pausedDuration?: number
127 | onShow?: () => void
128 | onHide?: () => void
129 | onPress?: () => void
130 | progressBarColor?: string
131 | backgroundColor?: string
132 | textColor?: string
133 | iconColor?: string
134 | iconSize?: number
135 | icon?: string | ReactNode
136 | iconFamily?: IconFamily
137 | theme?: 'light' | 'dark'
138 | useModal?: boolean
139 | }
140 |
141 | export interface AnimationConfig {
142 | [key: string]: {
143 | animateIn: any
144 | animateOut: any
145 | }
146 | }
147 |
--------------------------------------------------------------------------------