DEV Community

Oliver Reed
Oliver Reed

Posted on

Advanced Alert Dialogs and Modals with SweetAlert2 in React

SweetAlert2 is a beautiful, responsive, customizable, and accessible replacement for JavaScript's popup boxes. It provides a modern alternative to native browser alerts with extensive customization options, animations, and support for complex interactions like forms, file uploads, and async operations. This guide walks through advanced usage of SweetAlert2 with React, including custom configurations, React hooks integration, and complex dialog patterns. This is part 30 of a series on using SweetAlert2 with React.

Prerequisites

Before you begin, make sure you have:

  • Node.js version 14.0 or higher installed
  • npm, yarn, or pnpm package manager
  • A React project (version 16.8 or higher) or create-react-app setup
  • Basic knowledge of React hooks (useState, useEffect, useCallback)
  • Familiarity with async/await and Promises
  • Understanding of CSS for custom styling

Installation

Install SweetAlert2 using your preferred package manager:

npm install sweetalert2
Enter fullscreen mode Exit fullscreen mode

Or with yarn:

yarn add sweetalert2
Enter fullscreen mode Exit fullscreen mode

Or with pnpm:

pnpm add sweetalert2
Enter fullscreen mode Exit fullscreen mode

You may also want to install the React wrapper for better integration:

npm install @sweetalert2/with-react
Enter fullscreen mode Exit fullscreen mode

After installation, your package.json should include:

{
  "dependencies": {
    "sweetalert2": "^11.0.0",
    "@sweetalert2/with-react": "^5.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Project Setup

Import SweetAlert2 styles in your main entry file or component:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import 'sweetalert2/src/sweetalert2.scss';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

Or if using CSS instead of SCSS:

// src/index.js
import 'sweetalert2/dist/sweetalert2.min.css';
Enter fullscreen mode Exit fullscreen mode

First Example / Basic Usage

Let's create a simple alert component. Create a new file src/AlertExample.jsx:

// src/AlertExample.jsx
import React from 'react';
import Swal from 'sweetalert2';

function AlertExample() {
  const showSuccessAlert = () => {
    Swal.fire({
      title: 'Success!',
      text: 'Operation completed successfully.',
      icon: 'success',
      confirmButtonText: 'OK'
    });
  };

  const showErrorAlert = () => {
    Swal.fire({
      title: 'Error!',
      text: 'Something went wrong.',
      icon: 'error',
      confirmButtonText: 'OK'
    });
  };

  const showWarningAlert = () => {
    Swal.fire({
      title: 'Warning!',
      text: 'Please review your input.',
      icon: 'warning',
      confirmButtonText: 'OK'
    });
  };

  return (
    <div style={{ padding: '20px' }}>
      <h2>Basic SweetAlert2 Examples</h2>
      <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
        <button onClick={showSuccessAlert}>Success Alert</button>
        <button onClick={showErrorAlert}>Error Alert</button>
        <button onClick={showWarningAlert}>Warning Alert</button>
      </div>
    </div>
  );
}

export default AlertExample;
Enter fullscreen mode Exit fullscreen mode

Understanding the Basics

SweetAlert2 provides several key features:

  • Promise-based API: All alerts return promises that resolve with user actions
  • Multiple button types: confirm, deny, cancel buttons
  • Custom HTML: Full HTML support in alert content
  • Input support: Text, email, password, number, select, radio, checkbox, file inputs
  • Validation: Built-in and custom validation functions
  • Animations: Customizable animations and transitions
  • Theming: Extensive styling options

Key concepts for React integration:

  • Async/Await: Use async/await for cleaner code with user interactions
  • State Management: Integrate with React state for dynamic content
  • Custom Hooks: Create reusable hooks for common alert patterns
  • Error Handling: Proper error handling in async operations

Here's an example with confirmation dialogs:

// src/ConfirmationExample.jsx
import React from 'react';
import Swal from 'sweetalert2';

function ConfirmationExample() {
  const handleDelete = async () => {
    const result = await Swal.fire({
      title: 'Are you sure?',
      text: "You won't be able to revert this!",
      icon: 'warning',
      showCancelButton: true,
      confirmButtonColor: '#3085d6',
      cancelButtonColor: '#d33',
      confirmButtonText: 'Yes, delete it!',
      cancelButtonText: 'Cancel'
    });

    if (result.isConfirmed) {
      // Perform delete operation
      await Swal.fire('Deleted!', 'Your item has been deleted.', 'success');
    } else if (result.isDismissed) {
      // User cancelled
      Swal.fire('Cancelled', 'Your item is safe.', 'info');
    }
  };

  const handleSave = async () => {
    const result = await Swal.fire({
      title: 'Do you want to save the changes?',
      showDenyButton: true,
      showCancelButton: true,
      confirmButtonText: 'Save',
      denyButtonText: "Don't save",
      cancelButtonText: 'Cancel'
    });

    if (result.isConfirmed) {
      // Save operation
      await Swal.fire('Saved!', '', 'success');
    } else if (result.isDenied) {
      Swal.fire('Changes are not saved', '', 'info');
    }
  };

  return (
    <div style={{ padding: '20px' }}>
      <h2>Confirmation Dialogs</h2>
      <div style={{ display: 'flex', gap: '10px' }}>
        <button onClick={handleDelete}>Delete Item</button>
        <button onClick={handleSave}>Save Changes</button>
      </div>
    </div>
  );
}

export default ConfirmationExample;
Enter fullscreen mode Exit fullscreen mode

Practical Example / Building Something Real

Let's build a comprehensive form modal with validation, file upload, and async operations:

// src/AdvancedFormModal.jsx
import React, { useState, useCallback } from 'react';
import Swal from 'sweetalert2';

function AdvancedFormModal() {
  const [isProcessing, setIsProcessing] = useState(false);

  const showUserForm = useCallback(async () => {
    const { value: formValues } = await Swal.fire({
      title: 'Create New User',
      html: `
        <input id="swal-input1" class="swal2-input" placeholder="First Name" required>
        <input id="swal-input2" class="swal2-input" placeholder="Last Name" required>
        <input id="swal-input3" class="swal2-input" type="email" placeholder="Email" required>
        <input id="swal-input4" class="swal2-input" type="tel" placeholder="Phone Number">
        <select id="swal-input5" class="swal2-select">
          <option value="">Select Role</option>
          <option value="admin">Admin</option>
          <option value="user">User</option>
          <option value="guest">Guest</option>
        </select>
      `,
      focusConfirm: false,
      showCancelButton: true,
      confirmButtonText: 'Create User',
      cancelButtonText: 'Cancel',
      preConfirm: () => {
        const firstName = document.getElementById('swal-input1').value.trim();
        const lastName = document.getElementById('swal-input2').value.trim();
        const email = document.getElementById('swal-input3').value.trim();
        const phone = document.getElementById('swal-input4').value.trim();
        const role = document.getElementById('swal-input5').value;

        // Validation
        if (!firstName) {
          Swal.showValidationMessage('First name is required');
          return false;
        }
        if (!lastName) {
          Swal.showValidationMessage('Last name is required');
          return false;
        }
        if (!email) {
          Swal.showValidationMessage('Email is required');
          return false;
        }
        if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
          Swal.showValidationMessage('Please enter a valid email address');
          return false;
        }
        if (!role) {
          Swal.showValidationMessage('Please select a role');
          return false;
        }

        return {
          firstName,
          lastName,
          email,
          phone,
          role
        };
      },
      didOpen: () => {
        // Focus first input
        document.getElementById('swal-input1').focus();
      }
    });

    if (formValues) {
      await handleCreateUser(formValues);
    }
  }, []);

  const handleCreateUser = async (userData) => {
    setIsProcessing(true);

    // Show loading state
    Swal.fire({
      title: 'Creating user...',
      allowOutsideClick: false,
      didOpen: () => {
        Swal.showLoading();
      }
    });

    try {
      // Simulate API call
      await new Promise(resolve => setTimeout(resolve, 2000));

      // Simulate success
      await Swal.fire({
        icon: 'success',
        title: 'User Created!',
        html: `
          <p><strong>Name:</strong> ${userData.firstName} ${userData.lastName}</p>
          <p><strong>Email:</strong> ${userData.email}</p>
          <p><strong>Role:</strong> ${userData.role}</p>
        `,
        confirmButtonText: 'OK'
      });
    } catch (error) {
      await Swal.fire({
        icon: 'error',
        title: 'Error',
        text: 'Failed to create user. Please try again.',
        confirmButtonText: 'OK'
      });
    } finally {
      setIsProcessing(false);
    }
  };

  const showFileUpload = useCallback(async () => {
    const { value: file } = await Swal.fire({
      title: 'Upload Profile Picture',
      input: 'file',
      inputAttributes: {
        accept: 'image/*',
        'aria-label': 'Upload your profile picture'
      },
      showCancelButton: true,
      confirmButtonText: 'Upload',
      cancelButtonText: 'Cancel'
    });

    if (file) {
      const reader = new FileReader();
      reader.onload = async (e) => {
        await Swal.fire({
          title: 'Preview',
          imageUrl: e.target.result,
          imageAlt: 'Uploaded picture',
          showConfirmButton: true,
          confirmButtonText: 'Use this image'
        });
      };
      reader.readAsDataURL(file);
    }
  }, []);

  const showAsyncOperation = useCallback(async () => {
    let timerInterval;

    const result = await Swal.fire({
      title: 'Processing your request',
      html: 'This will take a few seconds...',
      timer: 5000,
      timerProgressBar: true,
      didOpen: () => {
        Swal.showLoading();
        const timer = Swal.getPopup().querySelector('b');
        timerInterval = setInterval(() => {
          timer.textContent = Swal.getTimerLeft();
        }, 100);
      },
      willClose: () => {
        clearInterval(timerInterval);
      }
    });

    if (result.dismiss === Swal.DismissReason.timer) {
      await Swal.fire({
        icon: 'success',
        title: 'Operation completed!',
        text: 'Your request has been processed.',
        confirmButtonText: 'OK'
      });
    }
  }, []);

  const showCustomStyled = useCallback(() => {
    Swal.fire({
      title: 'Custom Styled Alert',
      text: 'This alert has custom styling',
      icon: 'info',
      customClass: {
        popup: 'custom-popup',
        title: 'custom-title',
        confirmButton: 'custom-confirm-button'
      },
      buttonsStyling: false,
      confirmButtonText: 'Got it!',
      background: '#f0f0f0',
      color: '#333'
    });
  }, []);

  return (
    <div style={{ padding: '20px' }}>
      <h1>Advanced SweetAlert2 Examples</h1>
      <div style={{ display: 'flex', flexDirection: 'column', gap: '10px', maxWidth: '300px' }}>
        <button
          onClick={showUserForm}
          disabled={isProcessing}
          style={{
            padding: '10px 20px',
            backgroundColor: '#007bff',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: isProcessing ? 'not-allowed' : 'pointer'
          }}
        >
          Create User Form
        </button>
        <button
          onClick={showFileUpload}
          style={{
            padding: '10px 20px',
            backgroundColor: '#28a745',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Upload File
        </button>
        <button
          onClick={showAsyncOperation}
          style={{
            padding: '10px 20px',
            backgroundColor: '#ffc107',
            color: 'black',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Async Operation
        </button>
        <button
          onClick={showCustomStyled}
          style={{
            padding: '10px 20px',
            backgroundColor: '#6c757d',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Custom Styled
        </button>
      </div>
    </div>
  );
}

export default AdvancedFormModal;
Enter fullscreen mode Exit fullscreen mode

Now, create a custom React hook for reusable alert patterns:

// src/hooks/useSweetAlert.js
import { useCallback } from 'react';
import Swal from 'sweetalert2';

export const useSweetAlert = () => {
  const showSuccess = useCallback((title, text = '') => {
    return Swal.fire({
      icon: 'success',
      title,
      text,
      confirmButtonText: 'OK'
    });
  }, []);

  const showError = useCallback((title, text = '') => {
    return Swal.fire({
      icon: 'error',
      title,
      text,
      confirmButtonText: 'OK'
    });
  }, []);

  const showConfirm = useCallback(async (options) => {
    const result = await Swal.fire({
      title: options.title || 'Are you sure?',
      text: options.text || '',
      icon: options.icon || 'warning',
      showCancelButton: true,
      confirmButtonColor: options.confirmColor || '#3085d6',
      cancelButtonColor: options.cancelColor || '#d33',
      confirmButtonText: options.confirmText || 'Yes',
      cancelButtonText: options.cancelText || 'Cancel'
    });

    return result.isConfirmed;
  }, []);

  const showLoading = useCallback((title = 'Loading...') => {
    Swal.fire({
      title,
      allowOutsideClick: false,
      allowEscapeKey: false,
      didOpen: () => {
        Swal.showLoading();
      }
    });
  }, []);

  const closeAlert = useCallback(() => {
    Swal.close();
  }, []);

  return {
    showSuccess,
    showError,
    showConfirm,
    showLoading,
    closeAlert
  };
};
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

// src/App.jsx
import React from 'react';
import AdvancedFormModal from './AdvancedFormModal';
import './App.css';

function App() {
  return (
    <div className="App">
      <AdvancedFormModal />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Add custom styles in App.css:

/* src/App.css */
.custom-popup {
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}

.custom-title {
  font-size: 24px;
  font-weight: 600;
  color: #2c3e50;
}

.custom-confirm-button {
  background-color: #007bff;
  border-radius: 6px;
  padding: 10px 24px;
  font-weight: 500;
}
Enter fullscreen mode Exit fullscreen mode

Common Issues / Troubleshooting

  1. Styles not loading: Make sure you import SweetAlert2 styles in your entry file. Use import 'sweetalert2/dist/sweetalert2.min.css' for CSS or import 'sweetalert2/src/sweetalert2.scss' for SCSS.

  2. Promise handling: Always use await or .then() when you need to handle user responses. SweetAlert2 methods return promises.

  3. Validation not working: Ensure preConfirm returns false or calls Swal.showValidationMessage() for invalid inputs. Return the data object for valid inputs.

  4. Multiple alerts stacking: SweetAlert2 handles multiple alerts automatically, but you can prevent stacking by checking if an alert is already open using Swal.isVisible().

  5. Custom HTML not rendering: Use the html property instead of text for HTML content. Be careful with XSS vulnerabilities when using user-generated content.

  6. File upload issues: For file inputs, use the input: 'file' option and handle the FileReader API for previews. Remember that file inputs return File objects, not strings.

Next Steps

Now that you have an advanced understanding of SweetAlert2:

  • Explore advanced animations and transitions
  • Learn about custom themes and styling
  • Implement queue management for multiple alerts
  • Add internationalization (i18n) support
  • Integrate with React Context for global alert management
  • Learn about other notification libraries (react-toastify, notistack)
  • Check the official repository: https://github.com/sweetalert2/sweetalert2
  • Look for part 31 of this series for more advanced topics

Summary

You've successfully integrated SweetAlert2 into your React application with advanced features including form validation, file uploads, async operations, and custom styling. SweetAlert2 provides a powerful, accessible alternative to native browser alerts with extensive customization options.

SEO Keywords

sweetalert2
React alert library
sweetalert2 tutorial
React modal dialogs
sweetalert2 installation
React confirmation dialogs
sweetalert2 example
React alert notifications
sweetalert2 forms
React custom alerts
sweetalert2 validation
React async alerts
sweetalert2 file upload
React alert hooks
sweetalert2 getting started

Top comments (0)