title: How to integrate Jitsi-Meet with reactjs web
body: <p>I got a requirement to integrate jitsi-meet with reactjs web in my project. I went through below docs</p>
  <p><a href="https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe" rel="nofollow noreferrer">https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe</a></p>
  <p><a href="https://dev.to/fifo/video-conferencing-app-using-react-and-jitsi-32oh" rel="nofollow noreferrer">https://dev.to/fifo/video-conferencing-app-using-react-and-jitsi-32oh</a></p>
  <p><a href="https://github.com/jitsi/lib-jitsi-meet/tree/master/doc/example" rel="nofollow noreferrer">https://github.com/jitsi/lib-jitsi-meet/tree/master/doc/example</a></p>
  <p>From the third link, I was able to replicate it and was working fine.</p>
  <p>But I need to do the same thing reactjs for which i have installed two packages from npm</p>
  <ol>
  <li>npm i lib-jitsi-react</li>
  <li>npm i strophejs-plugins</li>
  </ol>
  <p>still, it did not work was throwing errors related stophejs(Missing strophe-plugins (disco and caps plugins are required)!).</p>
  <p>if anyone implemented jitsi meet in reactjs please guide me on how can I achieve this.</p>
  <p>And I used external API iframe also its working fine but I need more control on UI.</p>
  <p>Below is my react Component :</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>/* global $, JitsiMeetJS */
  import react from "react";
  import { JitsiMeetJS } from "lib-jitsi-meet";
  import $ from "jquery";
  
  export const options = {
    hosts: {
      domain: "beta.meet.jit.si",
      muc: "conference.beta.meet.jit.si", // FIXME: use XEP-0030
    },
    bosh: "https://beta.meet.jit.si/http-bind", // FIXME: use xep-0156 for that
  
    // The name of client node advertised in XEP-0115 'c' stanza
    clientNode: "http://jitsi.org/jitsimeet",
  };
  
  const confOptions = {
    openBridgeChannel: true,
  };
  
  let connection = null;
  let isJoined = false;
  let room = null;
  
  let localTracks = [];
  const remoteTracks = {};
  
  /**
   * Handles local tracks.
   * @param tracks Array with JitsiTrack objects
   */
  function onLocalTracks(tracks) {
    localTracks = tracks;
    for (let i = 0; i &lt; localTracks.length; i++) {
      localTracks[i].addEventListener(
        JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
        (audioLevel) =&gt; console.log(`Audio Level local: ${audioLevel}`)
      );
      localTracks[i].addEventListener(
        JitsiMeetJS.events.track.TRACK_MUTE_CHANGED,
        () =&gt; console.log("local track muted")
      );
      localTracks[i].addEventListener(
        JitsiMeetJS.events.track.LOCAL_TRACK_STOPPED,
        () =&gt; console.log("local track stoped")
      );
      localTracks[i].addEventListener(
        JitsiMeetJS.events.track.TRACK_AUDIO_OUTPUT_CHANGED,
        (deviceId) =&gt;
          console.log(`track audio output device was changed to ${deviceId}`)
      );
      if (localTracks[i].getType() === "video") {
        $("body").append(`&lt;video autoplay='1' id='localVideo${i}' /&gt;`);
        localTracks[i].attach($(`#localVideo${i}`)[0]);
      } else {
        $("body").append(
          `&lt;audio autoplay='1' muted='true' id='localAudio${i}' /&gt;`
        );
        localTracks[i].attach($(`#localAudio${i}`)[0]);
      }
      if (isJoined) {
        room.addTrack(localTracks[i]);
      }
    }
  }
  
  /**
   * Handles remote tracks
   * @param track JitsiTrack object
   */
  function onRemoteTrack(track) {
    if (track.isLocal()) {
      return;
    }
    const participant = track.getParticipantId();
  
    if (!remoteTracks[participant]) {
      remoteTracks[participant] = [];
    }
    const idx = remoteTracks[participant].push(track);
  
    track.addEventListener(
      JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
      (audioLevel) =&gt; console.log(`Audio Level remote: ${audioLevel}`)
    );
    track.addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED, () =&gt;
      console.log("remote track muted")
    );
    track.addEventListener(JitsiMeetJS.events.track.LOCAL_TRACK_STOPPED, () =&gt;
      console.log("remote track stoped")
    );
    track.addEventListener(
      JitsiMeetJS.events.track.TRACK_AUDIO_OUTPUT_CHANGED,
      (deviceId) =&gt;
        console.log(`track audio output device was changed to ${deviceId}`)
    );
    const id = participant + track.getType() + idx;
  
    if (track.getType() === "video") {
      $("body").append(`&lt;video autoplay='1' id='${participant}video${idx}' /&gt;`);
    } else {
      $("body").append(`&lt;audio autoplay='1' id='${participant}audio${idx}' /&gt;`);
    }
    track.attach($(`#${id}`)[0]);
  }
  
  /**
   * That function is executed when the conference is joined
   */
  function onConferenceJoined() {
    console.log("conference joined!");
    isJoined = true;
    for (let i = 0; i &lt; localTracks.length; i++) {
      room.addTrack(localTracks[i]);
    }
  }
  
  /**
   *
   * @param id
   */
  function onUserLeft(id) {
    console.log("user left");
    if (!remoteTracks[id]) {
      return;
    }
    const tracks = remoteTracks[id];
  
    for (let i = 0; i &lt; tracks.length; i++) {
      tracks[i].detach($(`#${id}${tracks[i].getType()}`));
    }
  }
  
  /**
   * That function is called when connection is established successfully
   */
  function onConnectionSuccess() {
    room = connection.initJitsiConference("s123", confOptions);
    room.on(JitsiMeetJS.events.conference.TRACK_ADDED, onRemoteTrack);
    room.on(JitsiMeetJS.events.conference.TRACK_REMOVED, (track) =&gt; {
      console.log(`track removed!!!${track}`);
    });
    room.on(JitsiMeetJS.events.conference.CONFERENCE_JOINED, onConferenceJoined);
    room.on(JitsiMeetJS.events.conference.USER_JOINED, (id) =&gt; {
      console.log("user join");
      remoteTracks[id] = [];
    });
    room.on(JitsiMeetJS.events.conference.USER_LEFT, onUserLeft);
    room.on(JitsiMeetJS.events.conference.TRACK_MUTE_CHANGED, (track) =&gt; {
      console.log(`${track.getType()} - ${track.isMuted()}`);
    });
    room.on(
      JitsiMeetJS.events.conference.DISPLAY_NAME_CHANGED,
      (userID, displayName) =&gt; console.log(`${userID} - ${displayName}`)
    );
    room.on(
      JitsiMeetJS.events.conference.TRACK_AUDIO_LEVEL_CHANGED,
      (userID, audioLevel) =&gt; console.log(`${userID} - ${audioLevel}`)
    );
    room.on(JitsiMeetJS.events.conference.PHONE_NUMBER_CHANGED, () =&gt;
      console.log(`${room.getPhoneNumber()} - ${room.getPhonePin()}`)
    );
    room.join();
  }
  
  /**
   * This function is called when the connection fail.
   */
  function onConnectionFailed() {
    console.error("Connection Failed!");
  }
  
  /**
   * This function is called when the connection fail.
   */
  function onDeviceListChanged(devices) {
    console.info("current devices", devices);
  }
  
  /**
   * This function is called when we disconnect.
   */
  function disconnect() {
    console.log("disconnect!");
    connection.removeEventListener(
      JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED,
      onConnectionSuccess
    );
    connection.removeEventListener(
      JitsiMeetJS.events.connection.CONNECTION_FAILED,
      onConnectionFailed
    );
    connection.removeEventListener(
      JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED,
      disconnect
    );
  }
  
  /**
   *
   */
  function unload() {
    for (let i = 0; i &lt; localTracks.length; i++) {
      localTracks[i].dispose();
    }
    room.leave();
    connection.disconnect();
  }
  
  let isVideo = true;
  
  /**
   *
   */
  export function switchVideo() {
    // eslint-disable-line no-unused-vars
    isVideo = !isVideo;
    if (localTracks[1]) {
      localTracks[1].dispose();
      localTracks.pop();
    }
    JitsiMeetJS.createLocalTracks({
      devices: [isVideo ? "video" : "desktop"],
    })
      .then((tracks) =&gt; {
        localTracks.push(tracks[0]);
        localTracks[1].addEventListener(
          JitsiMeetJS.events.track.TRACK_MUTE_CHANGED,
          () =&gt; console.log("local track muted")
        );
        localTracks[1].addEventListener(
          JitsiMeetJS.events.track.LOCAL_TRACK_STOPPED,
          () =&gt; console.log("local track stoped")
        );
        localTracks[1].attach($("#localVideo1")[0]);
        room.addTrack(localTracks[1]);
      })
      .catch((error) =&gt; console.log(error));
  }
  
  /**
   *
   * @param selected
   */
  function changeAudioOutput(selected) {
    // eslint-disable-line no-unused-vars
    JitsiMeetJS.mediaDevices.setAudioOutputDevice(selected.value);
  }
  
  $(window).bind("beforeunload", unload);
  $(window).bind("unload", unload);
  
  // JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
  const initOptions = {
    disableAudioLevels: true,
  };
  
  window.JitsiMeetJS.init(initOptions);
  
  connection = new window.JitsiMeetJS.JitsiConnection(null, null, options);
  
  connection.addEventListener(
    JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED,
    onConnectionSuccess
  );
  connection.addEventListener(
    JitsiMeetJS.events.connection.CONNECTION_FAILED,
    onConnectionFailed
  );
  connection.addEventListener(
    JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED,
    disconnect
  );
  
  JitsiMeetJS.mediaDevices.addEventListener(
    JitsiMeetJS.events.mediaDevices.DEVICE_LIST_CHANGED,
    onDeviceListChanged
  );
  
  connection.connect();
  
  JitsiMeetJS.createLocalTracks({ devices: ["audio", "video"] })
    .then(onLocalTracks)
    .catch((error) =&gt; {
      throw error;
    });
  
  if (JitsiMeetJS.mediaDevices.isDeviceChangeAvailable("output")) {
    JitsiMeetJS.mediaDevices.enumerateDevices((devices) =&gt; {
      const audioOutputDevices = devices.filter((d) =&gt; d.kind === "audiooutput");
  
      if (audioOutputDevices.length &gt; 1) {
        $("#audioOutputSelect").html(
          audioOutputDevices
            .map((d) =&gt; `&lt;option value="${d.deviceId}"&gt;${d.label}&lt;/option&gt;`)
            .join("\n")
        );
  
        $("#audioOutputSelectWrapper").show();
      }
    });
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
    &lt;head lang="en"&gt;
      &lt;meta charset="UTF-8" /&gt;
      &lt;title&gt;&lt;/title&gt;
      &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"&gt;&lt;/script&gt;
      &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/strophe.js/1.2.16/strophe.min.js"&gt;&lt;/script&gt;
      &lt;script src="strophe.disco.min.js?v=1"&gt;&lt;/script&gt;
      &lt;script src="lib-jitsi-meet.min.js"&gt;&lt;/script&gt;
      &lt;script src="jitsi.js"&gt;&lt;/script&gt;
    &lt;/head&gt;
    &lt;body&gt;
      &lt;a href="#" onclick="unload()"&gt;Unload&lt;/a&gt;
      &lt;a href="#" onclick="switchVideo()"&gt;switchVideo&lt;/a&gt;
      &lt;div id="audioOutputSelectWrapper" style="display: none;"&gt;
        Change audio output device
        &lt;select
          id="audioOutputSelect"
          onchange="changeAudioOutput(this)"
        &gt;&lt;/select&gt;
      &lt;/div&gt;
      &lt;!-- &lt;video id="localVideo" autoplay="true"&gt;&lt;/video&gt; --&gt;
      &lt;!--&lt;audio id="localAudio" autoplay="true" muted="true"&gt;&lt;/audio&gt;--&gt;
    &lt;/body&gt;
  &lt;/html&gt;
  © 2020 GitHub, Inc.</code></pre>
  </div>
  </div>
  </p>
  <p>Thanks in Advance.</p>