Connect Modal
- ConnectModal.tsx
- ConnectModal.module.css
- ConnectButton.tsx
Overview
The Connect Modal serves as the primary gateway for wallet connections within the RitoSwap ecosystem. This modal component provides users with a comprehensive interface for selecting their preferred wallet provider, displaying WalletConnect QR codes for desktop connections, and managing the entire connection flow with visual feedback at each stage. As part of the Wallet UI modal collection, it delivers a polished connection experience that adapts seamlessly between desktop and mobile contexts.

Inspired by ConnectKit’s clean interface design, the Connect Modal extends beyond standard connection flows with custom branding elements, enhanced mobile workflows, and sophisticated state management. The component leverages Wagmi v2’s wallet detection capabilities combined with EIP-6963 standards to automatically discover and display available wallet providers with their respective logos and names. This modal can be triggered by any component that manages its open state, most commonly through the ConnectButton widget.
Mobile Deep Linking Architecture
The Connect Modal implements a specialized workflow for mobile devices that leverages WalletConnect’s deep linking protocol. When users on mobile browsers initiate a WalletConnect connection, the modal automatically transitions to a connecting state and triggers a deep link that opens the operating system’s app chooser. This allows users to select their preferred mobile wallet app and complete the connection without manual app switching.
WalletConnect Deep Linking GuideComponent Architecture
The Connect Modal demonstrates sophisticated state management that orchestrates multiple connection methods while providing clear visual feedback throughout the user journey. The component creates a portal-based overlay that renders above all other content, ensuring consistent accessibility regardless of the triggering component’s position in the DOM hierarchy.
Triggering Mechanisms
While the Connect Modal itself is a controlled component that responds to external state management, it’s most commonly triggered by the ConnectButton widget. When users click the ConnectButton, it updates its internal state to open the modal:

// In ConnectButton.tsx
const [isConnectModalOpen, setIsConnectModalOpen] = useState(false);
// Clicking the button opens the modal
<button onClick={() => setIsConnectModalOpen(true)}>
Connect Wallet
</button>
// Modal receives state through props
<ConnectModal
isOpen={isConnectModalOpen}
onClose={() => setIsConnectModalOpen(false)}
/>
This architecture ensures the modal remains decoupled from its triggers, allowing any component to control its visibility through simple boolean state management.
Wallet Provider Detection
The Connect Modal leverages Wagmi’s connector system enhanced with EIP-6963 provider discovery to automatically detect and display available wallet providers. This modern approach allows wallets to announce themselves to dApps, providing metadata including names and icons without requiring manual configuration:
const { connectors } = useConnect();
// Separate injected wallets from WalletConnect
const injectedConnectors = connectors.filter(
(c) => c.type === "injected" && c.id !== "injected"
);
const walletConnectConnector = connectors.find(
(c) => c.type === "walletConnect"
);
Each detected wallet provider automatically supplies its branding assets through the connector metadata, eliminating the need for hardcoded wallet information and ensuring the modal always displays current provider details.
Portal Rendering Strategy
The Connect Modal implements React’s portal pattern to ensure proper rendering above all other content:
return createPortal(
<>
<div className={styles.backdrop} onClick={onClose} />
<div className={styles.modalWrapper}>
{/* Modal content */}
</div>
</>,
document.body
);
This approach prevents z-index conflicts and ensures the modal always appears above other interface elements, regardless of nested component hierarchies or CSS stacking contexts.
Modal States and Visual Design
The Connect Modal implements six distinct states, each carefully designed to guide users through the connection process with clear visual feedback and appropriate actions.
Default State - Provider Selection

The default state presents users with a clean interface featuring the RitoSwap logo and available wallet options. Each wallet provider displays with its official icon and name, automatically retrieved through the EIP-6963 standard. The modal includes an educational link for users new to Web3 wallets and displays terms of service acknowledgment in the footer.
WalletConnect QR State (Desktop Only)

When desktop users select WalletConnect, the modal transitions to display a customized QR code. The QR implementation uses the react-qr-code
library with custom styling that incorporates RitoSwap’s brand colors and embeds the RitoSwap logo in the center:
<QRCode
value={qrUri}
size={198}
bgColor="#000000"
fgColor="var(--accent-color)"
level="M"
/>
The modal provides a copy-to-clipboard function for the WalletConnect URI, allowing users to manually share the connection string if needed.
Connecting State

During active connection attempts, the modal displays the selected wallet’s icon with animated loading dots and instructional text. For mobile WalletConnect connections, an “Open Wallet” button appears to re-trigger the deep link if needed. This state persists until the connection succeeds, fails, or the user cancels.
Error State

Connection failures trigger a brief error state displaying “Connection Unsuccessful” with the wallet icon. The modal automatically returns to the default state after 1.5 seconds, allowing users to retry with a different wallet or troubleshoot the issue.
Canceled State

When users explicitly reject a connection request in their wallet, the modal displays a cancellation message. This distinct state helps users understand that the connection wasn’t a technical failure but rather their own action, reducing confusion and support requests.
Get Wallet Educational State

New users who click “I don’t have a wallet yet” enter an educational state that explains wallet functionality with clear benefit points. This state includes a prominent call-to-action linking to Ethereum.org’s wallet finder, helping onboard users new to the Web3 ecosystem.
Technical Implementation
Wagmi v2 Hook Integration
The Connect Modal leverages essential Wagmi v2 hooks to manage wallet connections and state:
useConnect
provides the core connection functionality, including the list of available connectors, connection initiation, pending states, and error handling. The modal uses these values to orchestrate the entire connection flow.
useAccount
monitors the connection status to automatically close the modal upon successful connection, ensuring a smooth user experience without manual intervention.
The connection handler demonstrates careful orchestration of different wallet types:
const handleConnectorClick = useCallback(
async (connector: Connector) => {
if (connector.type === "walletConnect") {
const screenWidth = window.innerWidth;
if (screenWidth <= 730) {
// Mobile: Direct deep link
setModalState("connecting");
const provider = await connector.getProvider();
(provider as any).once("display_uri", (uri: string) => {
window.location.href = uri;
});
} else {
// Desktop: Show QR code
setModalState("walletconnect-qr");
}
} else {
// Injected wallet flow
setModalState("connecting");
}
connect({ connector });
},
[connect]
);
Interaction Patterns
The modal supports multiple dismissal methods to accommodate different user preferences and device types:
Click Outside: Users can tap the backdrop area to close the modal, following standard modal interaction patterns.
Close Button: A prominent × button in the top-right corner provides explicit dismissal control.
Swipe Gesture: On touch devices, users can swipe left to dismiss the modal, enhancing mobile usability:
const swipeHandlers = useSwipeable({
onSwipedLeft: () => onClose(),
preventScrollOnSwipe: true,
delta: { left: 50 },
swipeDuration: 300,
});
Automatic Closure: The modal automatically closes upon successful connection, streamlining the user flow.
Props Reference
Prop | Type | Required | Description |
---|---|---|---|
isOpen | boolean | Yes | Controls the modal’s visibility. When true, the modal renders with animation. When false, the modal unmounts from the DOM. |
onClose | () => void | Yes | Callback function invoked when the modal should close. Called on backdrop click, close button click, swipe gesture, or successful connection. |
CSS Architecture
The modal’s styling architecture creates distinct visual states while maintaining smooth transitions between them.
State-Based Modal Sizing
The modal dynamically adjusts its size based on the current state:
/* Default state - full featured */
.modal {
width: 420px;
max-width: 90vw;
}
/* Loading states - compact */
.modalLoading {
width: 300px;
max-width: 90vw;
padding: 3rem 2rem;
}
This adaptive sizing provides focused interfaces for each state while maintaining visual consistency.
Animation System
The modal implements a sophisticated animation system with multiple layers:
/* Backdrop fade */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Modal entrance */
@keyframes slideIn {
from {
opacity: 0;
transform: translate(-50%, -45%);
}
to {
opacity: 1;
transform: translate(-50%, -50%);
}
}
/* QR code appearance */
@keyframes qrFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
These layered animations create a polished appearance that guides user attention appropriately.
Brand Integration
The modal incorporates RitoSwap’s visual identity through consistent use of brand colors, the prominent logo display, and the customized QR code styling that uses the accent color (red) for the QR pattern while embedding the RitoSwap logo in the center.
Usage Examples
Basic Implementation with ConnectButton
import ConnectButton from '@/components/utilities/wallet/connectButton/ConnectButton';
import ConnectModal from '@/components/utilities/wallet/connectModal/ConnectModal';
function Navigation() {
const [isConnectModalOpen, setIsConnectModalOpen] = useState(false);
return (
<>
<ConnectButton onClick={() => setIsConnectModalOpen(true)} />
<ConnectModal
isOpen={isConnectModalOpen}
onClose={() => setIsConnectModalOpen(false)}
/>
</>
);
}
Custom Trigger Implementation
// Any component can trigger the modal
function CustomConnectTrigger() {
const [showModal, setShowModal] = useState(false);
return (
<>
<button
onClick={() => setShowModal(true)}
className={styles.customTrigger}
>
Connect to Web3
</button>
<ConnectModal
isOpen={showModal}
onClose={() => setShowModal(false)}
/>
</>
);
}
Integration with Auth Flow
// Using with authentication requirements
function ProtectedSection() {
const { isConnected } = useAccount();
const [showConnectModal, setShowConnectModal] = useState(false);
useEffect(() => {
if (!isConnected) {
setShowConnectModal(true);
}
}, [isConnected]);
return (
<>
<ConnectModal
isOpen={showConnectModal && !isConnected}
onClose={() => setShowConnectModal(false)}
/>
{isConnected && <ProtectedContent />}
</>
);
}
Best Practices
When implementing the Connect Modal, ensure proper state management in the parent component. The modal itself is stateless for visibility control and relies entirely on props, making it predictable and easy to test.
Consider the user journey when deciding when to trigger the modal. While it can be opened proactively for protected sections, avoid showing it immediately on page load as this can be jarring for users who are simply browsing.
Test the modal behavior across different wallet types and connection methods. The experience differs significantly between injected wallets (MetaMask extension) and WalletConnect flows, particularly on mobile devices where deep linking behavior varies by browser and wallet app.
Monitor connection success rates in production. Understanding which wallets users prefer and where connections fail helps optimize the wallet options presented and can inform decisions about which providers to prioritize in the interface.
The Connect Modal’s automatic detection of wallet providers through EIP-6963 ensures that new wallets can integrate with RitoSwap without any code changes. As the Web3 wallet ecosystem evolves, the modal will automatically display newly available options, future-proofing the connection experience.