Skip to content

Part 1: Hello VueSIP ​

Time: 5 minutes | Difficulty: Beginner

In this part, you'll go from zero to making your first simulated call. By the end, you'll understand the core VueSIP concepts and have a working component.

What You'll Learn ​

  • Installing VueSIP
  • Using useSipMock for simulated calls
  • Reactive call state management
  • Basic call operations (connect, call, hangup)

Step 1: Install VueSIP ​

First, add VueSIP to your Vue 3 project:

bash
npm install vuesip
bash
pnpm add vuesip
bash
yarn add vuesip

Step 2: Create Your First Component ​

Create a new file HelloVueSIP.vue:

vue
<template>
  <div class="hello-vuesip">
    <h2>Hello VueSIP!</h2>

    <!-- Connection Status -->
    <div class="status">
      <span :class="{ connected: isConnected }">
        {{ isConnected ? 'Connected' : 'Disconnected' }}
      </span>
      <span v-if="isRegistered" class="registered">Registered</span>
    </div>

    <!-- Call State -->
    <div v-if="activeCall" class="call-info">
      <p><strong>Call State:</strong> {{ callState }}</p>
      <p><strong>To:</strong> {{ activeCall.remoteNumber }}</p>
      <p v-if="activeCall.duration > 0"><strong>Duration:</strong> {{ activeCall.duration }}s</p>
    </div>

    <!-- Controls -->
    <div class="controls">
      <button v-if="!isConnected" @click="handleConnect">Connect</button>

      <template v-else>
        <button v-if="callState === 'idle'" @click="handleCall">Call 555-1234</button>

        <button v-if="callState !== 'idle'" @click="handleHangup" class="hangup">Hang Up</button>
      </template>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useSipMock } from 'vuesip'

// Initialize mock SIP client
const { isConnected, isRegistered, callState, activeCall, connect, call, hangup } = useSipMock()

// Connect to simulated server
async function handleConnect() {
  try {
    await connect()
    console.log('Connected to mock SIP server!')
  } catch (error) {
    console.error('Connection failed:', error)
  }
}

// Make a call
async function handleCall() {
  try {
    const callId = await call('555-1234', 'Test Contact')
    console.log('Call started:', callId)
  } catch (error) {
    console.error('Call failed:', error)
  }
}

// End the call
async function handleHangup() {
  await hangup()
  console.log('Call ended')
}
</script>

<style scoped>
.hello-vuesip {
  max-width: 400px;
  margin: 2rem auto;
  padding: 1.5rem;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  font-family: system-ui, sans-serif;
}

.status {
  display: flex;
  gap: 1rem;
  margin-bottom: 1rem;
  padding: 0.5rem;
  background: #f8fafc;
  border-radius: 4px;
}

.status span {
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-size: 0.875rem;
}

.status .connected {
  background: #dcfce7;
  color: #166534;
}

.status .registered {
  background: #dbeafe;
  color: #1e40af;
}

.call-info {
  padding: 1rem;
  background: #f0fdf4;
  border-radius: 4px;
  margin-bottom: 1rem;
}

.call-info p {
  margin: 0.25rem 0;
}

.controls {
  display: flex;
  gap: 0.5rem;
}

button {
  flex: 1;
  padding: 0.75rem 1rem;
  border: none;
  border-radius: 4px;
  background: #3b82f6;
  color: white;
  font-size: 1rem;
  cursor: pointer;
  transition: background 0.2s;
}

button:hover {
  background: #2563eb;
}

button.hangup {
  background: #ef4444;
}

button.hangup:hover {
  background: #dc2626;
}
</style>

Step 3: Add to Your App ​

Import and use the component in your App.vue or any parent component:

vue
<template>
  <HelloVueSIP />
</template>

<script setup lang="ts">
import HelloVueSIP from './components/HelloVueSIP.vue'
</script>

Step 4: Run and Test ​

Start your development server and try it out:

bash
npm run dev
  1. Click Connect - Watch the status change to "Connected" then "Registered"
  2. Click Call 555-1234 - Watch the call state progress: calling -> ringing -> active
  3. Click Hang Up - The call ends and state returns to idle

Watch the Console

Open your browser's developer console to see the log messages and understand the call lifecycle.

Understanding the Code ​

Let's break down the key concepts:

The useSipMock Composable ​

typescript
const {
  isConnected, // Ref<boolean> - WebSocket connection state
  isRegistered, // Ref<boolean> - SIP registration state
  callState, // ComputedRef<MockCallState> - Current call state
  activeCall, // Ref<MockCall | null> - Active call details
  connect, // () => Promise<void> - Connect to server
  call, // (number, displayName?) => Promise<string> - Make call
  hangup, // () => Promise<void> - End call
} = useSipMock()

Reactive State ​

All state is reactive - your template automatically updates when:

  • Connection status changes
  • Call state transitions (idle -> calling -> ringing -> active)
  • Call duration increments

Call States ​

The callState computed property returns one of:

StateDescription
idleNo active call
callingOutbound call initiated, waiting for ring
ringingRemote party's phone is ringing
activeCall is connected
heldCall is on hold
endedCall has terminated

What You Learned ​

  • Installation: Adding VueSIP to a Vue 3 project
  • useSipMock: The mock mode composable for learning and testing
  • Reactive State: How VueSIP exposes reactive refs and computed properties
  • Call Operations: Basic connect, call, and hangup operations

Common Mistakes ​

Forgetting await

Always await the async operations. Without it, you might check state before the operation completes:

typescript
// Wrong - state checked before connect completes
connect()
console.log(isConnected.value) // false!

// Correct
await connect()
console.log(isConnected.value) // true!

Calling Before Connected

Attempting to make a call before connecting throws an error:

typescript
// Wrong - not connected yet
await call('555-1234') // Error: Not connected to SIP server

// Correct
await connect()
await call('555-1234') // Works!

Checking isConnected Before Registration

The mock server has two states - connected and registered. You need both for calls:

typescript
// isConnected = true means WebSocket is open
// isRegistered = true means you can make/receive calls
if (isConnected.value && isRegistered.value) {
  // Safe to make calls
}

Try It: Exercises ​

Before moving to Part 2, try these exercises to reinforce your learning:

Exercise 1: Display Call Progress ​

Add a visual indicator that shows the call state transitions with different colors:

  • calling - Yellow
  • ringing - Blue
  • active - Green
Solution
vue
<div :class="['call-state', callState]">
  {{ callState }}
</div>

<style scoped>
.call-state.calling {
  background: #fef3c7;
  color: #92400e;
}
.call-state.ringing {
  background: #dbeafe;
  color: #1e40af;
}
.call-state.active {
  background: #dcfce7;
  color: #166534;
}
</style>

Exercise 2: Add a Disconnect Button ​

Add a button that disconnects from the mock server:

Solution
vue
<button @click="disconnect">Disconnect</button>

<script setup lang="ts">
const { disconnect /* ... */ } = useSipMock()
</script>

Exercise 3: Custom Ring Delay ​

Configure the mock to have a longer ring delay (5 seconds instead of default 3):

Solution
typescript
const {
  /* ... */
} = useSipMock({
  ringDelay: 5000, // 5 seconds
})

Next Steps ​

Congratulations! You've made your first VueSIP call. In Part 2, we'll build a complete softphone with a dial pad, call controls, and proper UI.

Released under the MIT License.