Daily Client SDK for JavaScript


Daily's client SDK for JavaScript is called daily-js. The daily-js SDK can be used to build custom video or audio calling applications or embed and control Daily Prebuilt.

With this library you can:

  • Initialize your call settings
  • Join a room
  • Manage call lifecycle and participant state
  • Respond to in-call events
  • Customize call layout and UI (Daily Prebuilt)

Daily supports versions of daily-js and react-native-daily-js released in the past six months.

We recommend updating your Daily libraries regularly to access our latest features and to ensure the version you are using is currently supported.

Installing daily-js

To use daily-js, you can load the library in a <script> tag or bundle with webpack and other tools.

Loading the library in a script tag

You can use this library from a <script> tag, as a CommonJS-style module with require, or as an ES6-style module with import (including within a <script type="module"> context).

The easiest way to get started is to load this library from unpkg, and add a couple of lines of code to your web page or app.

Bundling with webpack and other tools

Of course, you can also use a bundler like webpack or rollup.

npm install @daily-co/daily-js

Then in your application code:

Getting started

This guide provides an overview of how to build a custom video or audio calling application using daily-js. For details on the methods and events mentioned in this guide, see the daily-js reference documentation.

If you prefer to jump right in, you can get the associated code and run the demo.

Website UI displays floating participant video screens

If you're interested in learning how to use daily-js to control Daily Prebuilt, check out the Prebuilt guide for demos and sample code.


Create a Daily account

Before you get started, you'll need to sign up for your free Daily account. You get 10,000 free minutes every month, so you can try out Daily without any cost.

Create a Daily room

Begin by navigating to Daily's developer dashboard and creating a new Daily room. If you need guidance, you can follow the steps in our room creation guide.

Once you've created your room, make note of the URL. You'll be using that later.

Create a call client

Daily's call client is your main interface into Daily functionality. It's the thing that your code will invoke to do anything: start a call, leave a call, listen for participants coming and going, get video and audio to play, etc.

A call client can be created with:

For more advanced use, the call client can be initialized with a number of arguments. Check out the reference docs for more information.

In our getting started demo, the call client is created as part of the DailyCallManager class which is used to initialize the object, set up event listeners and handlers, manage tracks, and manage devices.

Set up event listeners

Before joining the room, we'll continue initializing our application by setting up event listeners. In this app, we're concerned with handling participant state changes, specifically regarding audio / video tracks and local camera / microphone permissions.

daily-js follows the EventEmitter pattern to add event callbacks. To set up a listener, you can call on(). You can call off() to remove event callbacks. For example, you can call:

In our getting started demo, we'll handle a number of events in the setupEventListeners() method:

You can read more about the events that daily-js emits in the reference docs.

Join a room

Now that the call client is set up and initialized, we're ready to join a room. This can be done as follows:

join() can take a number of different arguments to configure the client for the upcoming call. Refer to our reference documentation for more information.

In our getting started demo, we join a room with either a url only or a url andtoken property. You can modify the code to experiment with different properties to see how they affect the call.

By default, the call to join() runs several operations automatically to make it easy to get started:

  • Calls getUserMedia() and configures your camera devices for 720p 30fps video
  • Enables the camera and microphone
  • Publishes camera and microphone tracks
  • Subscribes to media from all remote participants

The behavior above varies based on rooms settings. For example, if the room is configured to start with the camera disabled, the getUserMedia() call and publishing the camera video is deferred until the camera is enabled.

daily-js offers complete flexibility for all of these items. To learn more, check out our guides, demos, blog, and reference docs.

Can't find what you're looking for? Try the Ask AI feature on the bottom right of the docs page, post a question to our community, or contact us. We're glad to help!

Handle tracks

When the local client or remote participants join a room, performs an action like unmute their microphone, or leaves a call, media tracks may be started or stopped. Track state is contained within participants().

You can listen for changes to tracks through participant events: 'participant-joined', 'participant-updated', and 'participant-left'.

For even more details on this topic, check out our blog post on handling media tracks.

In our getting started demo, there are two handlers for these three events:

  • handleParticipantJoinedOrUpdated() handles the 'participant-joined' and 'participant-updated' events. This method is responsible for creating HTML elements for the video track, attaching and playing tracks, destroying tracks, updating participant count, and updating device states.
  • handleParticipantLeft() handles the 'participant-left' event. This method is responsible for destroying the video and audio tracks and related UI elements when a participant leaves the call.

When it comes to handling tracks our approach is to break up the task into three parts:

  1. When a participant joins, add HTML elements where their video will be placed once a track is available. For video apps, this step can be taken even if the participant keeps their camera off during the call as the visual elements give the user presence during a call.
  2. When a persistentTrack is available, set the corresponding <audio> or <video> element's srcObject to the MediaStreamTrack and play it.
  3. For video tracks, based on the track's state, show or hide the video element.

If you want to read more about the track states, check out the reference docs.

Here's how that handler, handleParticipantJoinedOrUpdated() works:

Let's inspect three of the methods that the handler calls:

  1. startOrUpdateTrack() is responsible for setting the target <video> element's srcObject to the MediaStreamTrack and playing it. The three scenarios the logic accounts for are:
  • The track is new, so a new srcObject needs to be provided.
  • The track has updated, so the srcObject needs to be updated to the new MediaStreamTrack.
  • The track is already attached, so take no action.

Following these points is important to ensure that the correct MediaStreamTrack is attached and that your code is not unnecessarily calling play(), which can result in a video flicker or audio blip.

  1. updateVideoUi() is responsible for changing the visual appearance of the video track, like showing or hiding it. In the demo code, the video track is shown when it's playable and hidden when it's off, interrupted, or blocked. You can customize the UI as needed in your application. For example, if the video track is blocked, you may want to notify the user and provide them with recovery steps.
  1. destroyTracks() is responsible for setting the srcObject to null and removing the corresponding track from the DOM. destroyTracks() is called when there is no longer a persistentTrack available for a given track type. This can happen when the local user turns off their camera or when a screen share is stopped.

When the participant leaves, the 'participant-left' event is emitted. The demo application's handler for this event is called handleParticipantLeft(). This handler is responsible for:

  • Cleaning up the media tracks: To do this, the application reuses the destroyTracks() method from above.
  • Removing related elements from the DOM: To do this, the application removes the participant's video-container from the DOM.

Control local media devices

You can obtain the state for the local participant's camera and microphone by calling the localVideo() and localAudio() methods, respectively. Separately, you can control the local client's camera and microphone using the setLocalVideo() and setLocalAudio() methods. When a device's state changes, a corresponding 'participant-updated' event is emitted. You can handle this event to make relevant changes to device controls in your UI.

In our getting started demo, there are camera and microphone buttons used to turn the devices on and off. There is also a text field indicating the state of the buttons. Here's an example of how the demo app controls the devices and updates the UI:

  • Controlling the camera, which is initiated by clicking the "Camera" button:
  • Controlling the microphone, which is initiated by clicking the "Microphone" button:
  • Updating the device UI, which is called by the handleParticipantJoinedOrUpdated() handler:

Change selected local media devices

Using daily-js, you can build a device selector to allow users to select which camera and microphone device to use during the call.

To start, you can select which devices are available to the user with the enumerateDevices() method. This will return to you the videoinput, audioinput, and audiooutput devices available to the user.

You'll also want to know which device, if any, the user has currently selected. For that, you can call the getInputDevices() method, which will return the currently selected camera, microphone, and speaker devices.

Lastly, you can use the setInputDevicesAsync() method to select an video or audio device to use.

In our getting started demo, we set up the device selector after receiving the 'joined-meeting' event, which indicates that the user is connected to the call. Here's example code showing how this is done:

You can set up a handler for device changes that happen after the user has joined by listening to the 'available-devices-updated' event. This event will return devices using the same format as the enumerateDevices() method.

Leave a room

Once the meeting is finished, the client can leave the meeting room by calling leave(). When leaving, be sure to set srcObjects to null to release media resources.

After leaving a meeting, the call object can be reused to join() another meeting, if you'd like to maintain the state of the call object. Once you've finished with the call object, we recommend calling destroy() to free all resources associated with it. Want more info? See our blog post covering leave() and destroy() best practices.

In our getting started demo, when we call leave(), we also remove audio and video elements from the DOM and reset status fields. For example:

Lastly, the demo app handles the local participant's 'left-meeting' event by destroying tracks and resetting the UI. That includes:

  • Disabling the toggle camera and mic buttons
  • Resetting the camera and mic selectors
  • Updating the call state (e.g. participant count and active speaker information) in the UI
  • Removing all video containers

Wrapping up

And with that, you have a basic video calling app! Hopefully this helps to jump start your development building video and audio calling apps with Daily.


If you're interested in seeing the demo code used in the getting started guide above, you can find it on GitHub.

We have many other demos, tutorials, and blog posts available to help teach you how to use daily-js. Here are some recommendations to get you started:

Custom application demos

Prebuilt demos

Didn't find what you're looking for? Check out our blog for more code and tutorials and our daily-demos GitHub repo for more demos.