Skip to main content
This guide builds a minimal but complete custom call: it joins a room, renders a tile per participant, and plays everyone’s audio. By the end you will have the full pattern that every Daily React app is built on.
You need a Daily room URL to follow along. Create a free account at daily.co and create a room to get a URL like https://your-domain.daily.co/room-name.

The shape of a Daily React app

Three pieces appear in every app:
  1. A DailyProvider that creates the call object from your room url and shares it with the tree.
  2. Hooks inside the provider that read call state.
  3. Components like DailyVideo and DailyAudio that render media.
1

Install the packages

Daily React needs @daily-co/daily-js and jotai as peer dependencies.
2

Wrap your app in DailyProvider

DailyProvider holds the call object and shares it with every component in your tree. The simplest setup is to let the provider create the call object for you: pass your room url straight to it.
3

Join and leave the room

Get the call object with useDaily and call join() when the component mounts. The room url is already configured on the provider, so join() needs no arguments. Returning a cleanup function that calls leave() makes leaving automatic when the component unmounts.
useMeetingState re-renders the component as the meeting moves through joining-meeting to joined-meeting, so you can show a loading state without listening for events yourself.
4

Render a tile for each participant

useParticipantIds returns the list of participant session IDs and updates as people join and leave. Map over it to render a tile per participant.
Render exactly one DailyAudio for the whole call, not one per tile. It manages speaker slots and autoplay for every remote participant in a single place.
5

Render each participant's video and name

DailyVideo renders a participant’s video track given their sessionId. useParticipantProperty reads just the user_name, so the tile only re-renders when that one property changes.

Complete example

A full working call. Set ROOM_URL to your room and render <App />.
App.jsx
That is the entire pattern: create a call object, provide it, then read state with hooks and render media with components. No event listeners, no manual track attachment, no cleanup boilerplate.

What just happened

  • You never wrote a single .on('participant-joined', ...) listener. useParticipantIds tracks that for you.
  • Muting a mic or changing a name re-renders only the affected tile, because useParticipantProperty subscribes to one property at a time.
  • Video and audio attach to the DOM through DailyVideo and DailyAudio, including the autoplay handling that is easy to get wrong by hand.

Next steps

Thinking in Daily React

Why the hooks re-render the way they do.

Rendering media

Video, audio, and screen tracks in depth.

Devices

Camera, mic, and speaker selection and toggling.

Working with participants

Filtering, sorting, and reading participant state.