react-webrtc-phone-dialer
v1.0.0
Published
A modern, floating WebRTC phone dialer component for React applications
Maintainers
Readme
React WebRTC Phone Dialer
A modern, floating WebRTC phone dialer component for React applications. Perfect for integrating voice calling functionality with services like Vonage, Twilio, or any WebRTC solution.
Features
- 🎯 Floating Window: Draggable, minimizable interface that doesn't interfere with your app
- 📞 Complete Call Management: Dial, answer, reject, and end calls
- 🔄 Dialing Screen: Visual feedback when calling and waiting for connection
- 👥 Contact Integration: Built-in address book with search functionality
- 📱 Modern UI: Clean, responsive design with light/dark theme support
- 🔊 Call Controls: Mute, speaker, and call duration tracking
- 📊 Call History: Track incoming, outgoing, and missed calls
- 🎨 Customizable: Easy theming and styling options
- 📦 TypeScript: Full TypeScript support with proper type definitions
Installation
npm install react-webrtc-phone-dialerQuick Start
import React, { useState } from "react";
import { PhoneDialer } from "react-webrtc-phone-dialer";
const App = () => {
const [isInCall, setIsInCall] = useState(false);
const [isDialing, setIsDialing] = useState(false);
const [dialingNumber, setDialingNumber] = useState("");
const [dialingCallId, setDialingCallId] = useState("");
const [incomingCall, setIncomingCall] = useState(null);
const contacts = [
{ id: "1", name: "John Doe", number: "+1234567890", avatar: "👨💼" },
{ id: "2", name: "Jane Smith", number: "+0987654321", avatar: "👩💼" },
];
const availableNumbers = ["+1555123456", "+1555987654"];
const callbacks = {
onCall: (number, fromNumber) => {
console.log(`Calling ${number} from ${fromNumber}`);
// Start dialing state
setIsDialing(true);
setDialingNumber(number);
setDialingCallId(`call-${Date.now()}`);
// Integrate with your WebRTC service here
// When call connects, set isInCall to true and isDialing to false
},
onEndCall: (callId) => {
console.log(`Ending call ${callId}`);
setIsInCall(false);
setIsDialing(false);
},
onCancelDialing: (callId) => {
console.log(`Canceling dialing ${callId}`);
setIsDialing(false);
setDialingNumber("");
setDialingCallId("");
},
onAnswerCall: (callId) => {
console.log(`Answering call ${callId}`);
setIncomingCall(null);
setIsInCall(true);
},
onRejectCall: (callId) => {
console.log(`Rejecting call ${callId}`);
setIncomingCall(null);
},
};
return (
<div>
<h1>My App</h1>
<PhoneDialer
availableNumbers={availableNumbers}
contacts={contacts}
isInCall={isInCall}
isDialing={isDialing}
dialingNumber={dialingNumber}
dialingCallId={dialingCallId}
incomingCall={incomingCall}
callbacks={callbacks}
/>
</div>
);
};
export default App;Props
Required Props
| Prop | Type | Description |
| ------------------ | ---------- | --------------------------------------------- |
| availableNumbers | string[] | Array of phone numbers the user can call from |
Optional Props
| Prop | Type | Default | Description |
| ----------------- | ------------------------ | ------------------------------------- | -------------------------------------- |
| contacts | Contact[] | [] | Array of contacts for the address book |
| callHistory | CallHistoryItem[] | [] | Array of call history items |
| incomingCall | IncomingCall \| null | null | Current incoming call (if any) |
| isInCall | boolean | false | Whether a call is currently active |
| isDialing | boolean | false | Whether a call is currently dialing |
| dialingNumber | string | '' | The number being dialed |
| dialingCallId | string | '' | Unique ID for the dialing call |
| initialPosition | {x: number, y: number} | {x: window.innerWidth - 400, y: 50} | Initial position of the dialer |
| isMinimized | boolean | false | Whether the dialer starts minimized |
| callbacks | CallCallbacks | {} | Event handlers for call actions |
| className | string | '' | Additional CSS class for styling |
| theme | 'light' \| 'dark' | 'light' | UI theme |
Type Definitions
interface Contact {
id: string;
name: string;
number: string;
avatar?: string;
}
interface CallHistoryItem {
id: string;
name: string;
number: string;
type: "incoming" | "outgoing" | "missed";
timestamp: Date;
duration: number; // in seconds
}
interface IncomingCall {
id: string;
name: string;
number: string;
avatar?: string;
}
interface CallCallbacks {
onCall?: (number: string, fromNumber: string) => void;
onAnswerCall?: (callId: string) => void;
onRejectCall?: (callId: string) => void;
onEndCall?: (callId: string) => void;
onCancelDialing?: (callId: string) => void;
onMute?: (isMuted: boolean) => void;
onSpeaker?: (isOn: boolean) => void;
}Call States
The phone dialer supports three main call states:
- Idle: Normal state when no call is active
- Dialing: When a call is being initiated and waiting for connection
- In Call: When a call is connected and active
Dialing Screen
When isDialing is true, the component displays a dedicated dialing screen with:
- Animated calling icon
- The number being dialed
- "Waiting for connection" status with animated dots
- Cancel button to abort the call
// Example: Managing dialing state
const [isDialing, setIsDialing] = useState(false);
const [dialingNumber, setDialingNumber] = useState("");
const handleCall = (number) => {
setIsDialing(true);
setDialingNumber(number);
// Your WebRTC service initiates the call
// When connected, set isDialing to false and isInCall to true
};Integration Examples
Vonage Integration
import { Client } from "@vonage/client-sdk";
const vonageClient = new Client();
const callbacks = {
onCall: async (number, fromNumber) => {
try {
const call = await vonageClient.createCall({
to: [{ type: "phone", number }],
from: { type: "phone", number: fromNumber },
});
setCurrentCall(call);
} catch (error) {
console.error("Call failed:", error);
}
},
onEndCall: () => {
if (currentCall) {
currentCall.hangup();
setCurrentCall(null);
}
},
};Twilio Integration
import { Device } from "@twilio/voice-sdk";
const device = new Device(twilioToken);
const callbacks = {
onCall: (number, fromNumber) => {
const call = device.connect({
To: number,
From: fromNumber,
});
setCurrentCall(call);
},
};Styling
The component comes with built-in styles, but you can customize it:
/* Override default styles */
.phone-dialer {
--primary-color: #your-brand-color;
--background-color: #your-bg-color;
}
/* Custom theme */
.my-custom-dialer {
border: 2px solid #your-border-color;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}Advanced Usage
Managing Call State
const [callState, setCallState] = useState({
isInCall: false,
currentCall: null,
incomingCall: null,
callDuration: 0,
});
// Simulate incoming call
useEffect(() => {
const simulateIncoming = () => {
setCallState((prev) => ({
...prev,
incomingCall: {
id: "incoming-123",
name: "John Doe",
number: "+1234567890",
avatar: "👨💼",
},
}));
};
// Your WebRTC service would trigger this
// vonageClient.on('callIncoming', simulateIncoming);
}, []);Persisting Dialer Position
const [dialerPosition, setDialerPosition] = useState(() => {
const saved = localStorage.getItem("dialerPosition");
return saved ? JSON.parse(saved) : { x: 100, y: 100 };
});
// Save position when component unmounts
useEffect(() => {
return () => {
localStorage.setItem("dialerPosition", JSON.stringify(dialerPosition));
};
}, [dialerPosition]);Browser Support
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
License
MIT © [Your Name]
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
