TypeError: Cannot read property 'appendChild' of null in jitsi meet external_api.js

i have used jitsi-meet external_api.js in my react web app. the following error occur when i want start video conference.
Cannot read property 'appendChild' of null at parentNode: document.getElementById('jitsi-container'), line of below VideoConference component.
here is code of component:

import React, { useState, useEffect } from 'react';
import ProgressComponent from '@material-ui/core/CircularProgress';

const VideoConference = () => {
  const [loading, setLoading] = useState(true);
  const [displayName, setDisplayName] = useState('')  //for username
  const [roomName, setRoomName] = useState('')//for roomname
  const [OnConference, setOnConference] = useState(false) //for starting conference
  
 
  const containerStyle = {
    width: '800px',
    height: '400px',
  };
  const jitsiContainerStyle = {
    display: (loading ? 'none' : 'block'),
    width: '100%',
    height: '100%',
  }
 
  function startConference() {
    try {
      
     const domain = "meet.jit.si";
     const options = {
      roomName: roomName,
      height: 400,
      parentNode: document.getElementById('jitsi-container'),
      interfaceConfigOverwrite: {
       filmStripOnly: false,
       SHOW_JITSI_WATERMARK: false,
      },
      configOverwrite: {
       disableSimulcast: false,
      },
     };
     const api = new JitsiMeetExternalAPI(domain, options);
     
     api.addEventListener('videoConferenceJoined', () => {
      console.log('Local User Joined');
      setLoading(false);
      api.executeCommand('displayName', displayName);
     });
    } catch (error) {
     console.error('Failed to load Jitsi API', error);
    }
  }
  useEffect(() => {
    // verify the JitsiMeetExternalAPI constructor is added to the global..
    if (window.JitsiMeetExternalAPI) startConference()
    else alert('Jitsi Meet API script not loaded');
   }, [OnConference]);//should start when user click on 'let 'start button
   useEffect(() => {
    // verify the JitsiMeetExternalAPI constructor is added to the global..
    if (!window.JitsiMeetExternalAPI) 
     alert('Jitsi Meet API script not loaded');
   }, []);
  
   
  // below code logic to display form or jitsi container
  
  return OnConference?
  (
      <div
       style={containerStyle}
      >
       {loading && <ProgressComponent />}
       <div
        id="jitsi-container"
        style={jitsiContainerStyle}
       />
      </div>
  
     )
  :(
  
     <div>
       <h1>Create a Meeting</h1>
        <input type='text' placeholder='Room name' value={roomName} onChange={e => setRoomName(e.target.value)} />
        <input type='text' placeholder='Your name' value={displayName} onChange={e => setDisplayName(e.target.value)} />
        <button onClick={() => setOnConference(true)}> Let&apos;s start!</button>
     </div>
   )
  
  }
  

export default VideoConference;

i want to show the container in the same place where form is displayed.

I see 2 problems with this code. The error comes from the fact that in your useEffect hook, you don’t check if OnConference is true. That hook is called when the component is initialized (as well as every time OnConference changes) and at that point the parentNode does not exist yet.

Second problem is that you use display: none on your parent node before the user joins the conference, but that event is emitted after the prejoin screen. You cannot go past the prejoin screen because you don’t see anything so you never actually show the iframe.

Hi @izakgl very thanks for help me .I removed both errors but now the iframe displays but user does not joins the conference and loading component does not go away .
here is my modified component:

import React, { useState, useEffect } from 'react';
import ProgressComponent from '@material-ui/core/CircularProgress';

const VideoConference = () => {
  const [loading, setLoading] = useState(true);
  const [displayName, setDisplayName] = useState('')  //for username
  const [roomName, setRoomName] = useState('')//for roomname
  const [password, setPassword] = useState('')//for password
  const [OnConference, setOnConference] = useState(false) //for starting conference
  const jitsiContainer="jitsi-container"
 
  const containerStyle = {
    width: '800px',
    height: '400px',
  };
  const jitsiContainerStyle = {
    //display: (loading ? 'none' : 'block'),
    width: '100%',
    height: '100%',
  }
 
  function startConference() {
    try {
      
     const domain = "meet.jit.si";
     const options = {
      roomName: roomName,
      password:password,
      height: 400,
      parentNode: document.getElementById(jitsiContainer),
      interfaceConfigOverwrite: {
       filmStripOnly: false,
       SHOW_JITSI_WATERMARK: false,
      },
      configOverwrite: {
       disableSimulcast: false,
      },
     };
     const api = new JitsiMeetExternalAPI(domain, options);
     api.addEventListener('videoConferenceJoined', () => {
      console.log('Local User Joined');
      setLoading(false);
      api.executeCommand('displayName', displayName);
     });
    } catch (error) {
     console.error('Failed to load Jitsi API', error);
    }
  }
  useEffect(() => {
    if (OnConference==true){
    // verify the JitsiMeetExternalAPI constructor is added to the global..
    if (window.JitsiMeetExternalAPI) startConference()
    else alert('Jitsi Meet API script not loaded');
    }
   }, [OnConference]);//should start when user click on 'let 'start button
   useEffect(() => {
    // verify the JitsiMeetExternalAPI constructor is added to the global..
    if (!window.JitsiMeetExternalAPI) 
     alert('Jitsi Meet API script not loaded');
   }, []);
  
   
  // below code logic to display form or jitsi container
  
  return OnConference?
  (
      <div
       style={containerStyle}
      >
       {loading && <ProgressComponent />}
       <div
        id="jitsi-container"  
        style={jitsiContainerStyle}
       />
      </div>
  
     )
  :(
  
     <div>
       <h1>Create a Meeting</h1>
        <input type='text' placeholder='Room name' value={roomName} onChange={e => setRoomName(e.target.value)} />
        <input type='text' placeholder='Your name' value={displayName} onChange={e => setDisplayName(e.target.value)} />
        <input type='text' placeholder='password' value={password} onChange={e => setPassword(e.target.value)} />
        <button onClick={() => setOnConference(true)}> Let&apos;s start!</button>
     </div>
   )
  
  }
  

export default VideoConference;

i think there is problem with following code

api.addEventListener('videoConferenceJoined', () => {
      console.log('Local User Joined');
      setLoading(false);
      api.executeCommand('displayName', displayName);

the first screen is being displayed which should not be displayed and direct conference should be displayed when the user joined.
thanks and regards.

I don’t think there’s any problem with that piece of code. Prejoin screen is enabled by default. If you wish to disable it, pass prejoinPageEnabled: false in the configOverwrite of your options objects. By the way, you can also set user displayName by passing userInfo object into your options with the property displayName.

Your options object would then look something like this:

const options = {
    roomName: roomName,
    password: password,
    height: 400,
    parentNode: document.getElementById(jitsiContainer),
    interfaceConfigOverwrite: {
        filmStripOnly: false,
        SHOW_JITSI_WATERMARK: false,
    },
    configOverwrite: {
        disableSimulcast: false,
        prejoinPageEnabled: false
    },
    userInfo: {
        displayName: displayName
    }
};

hi @izakgl thanks for helping. I have applied changes as suggested by you. the prejoinscreen still appear but videoconference does not started.
moreover the console does not show the result of line:

console.log('Local User Joined');

instead it shows error in info tab of console as:

app.bundle.min.js?v=4980.1616:200
 [Amplitude] Invalid domain option input type. Expected string but received undefined

the modified videoConferencecomponent is as follows:

import React, { useState, useEffect } from 'react';
import ProgressComponent from '@material-ui/core/CircularProgress';

const VideoConference = () => {
  const [loading, setLoading] = useState(true);
  const [displayName, setDisplayName] = useState('')  //for username
  const [roomName, setRoomName] = useState('')//for roomname
  const [password, setPassword] = useState('')//for password
  const [OnConference, setOnConference] = useState(false) //for starting conference
  const jitsiContainer="jitsi-container";
 
  const containerStyle = {
    width: '800px',
    height: '400px',
  };
  const jitsiContainerStyle = {
    //display: (loading ? 'none' : 'block'),
    width: '100%',
    height: '100%',
  }
 
  function startConference() {
    try {
      
     const domain = "meet.jit.si";
     const options = {
      roomName: roomName,
      password:password,
      height: 400,
      parentNode: document.getElementById(jitsiContainer),
      interfaceConfigOverwrite: {
       filmStripOnly: false,
       SHOW_JITSI_WATERMARK: false,
      },
      configOverwrite: {
       disableSimulcast: false,
       prejoinPageEnabled: false,
      },
      userInfo: {
        displayName: displayName
    }
     };
     const api = new JitsiMeetExternalAPI(domain, options);
     api.addEventListener('videoConferenceJoined', () => {
      console.log('Local User Joined');
     // api.executeCommand('displayName', displayName);
     setLoading(false);
     });
     // for ending the conference
     api.addEventListener('videoConferenceLeft', () => {
      navigate('/');
    });
    } catch (error) {
     console.error('Failed to load Jitsi API', error);
    }
  }
  useEffect(() => {
    if (OnConference==true){
    // verify the JitsiMeetExternalAPI constructor is added to the global..
    if (window.JitsiMeetExternalAPI) startConference()
    else alert('Jitsi Meet API script not loaded');
    }
   }, [OnConference]);//should start when user click on 'let 'start button
   useEffect(() => {
    // verify the JitsiMeetExternalAPI constructor is added to the global..
    if (!window.JitsiMeetExternalAPI) 
     alert('Jitsi Meet API script not loaded');
   }, []);
  
   
  // below code logic to display form or jitsi container
  
  return OnConference?
  (
      <div
       style={containerStyle}
      >
       {loading && <ProgressComponent />}
       <div
        id="jitsi-container"  
        style={jitsiContainerStyle}
       />
      </div>
  
     )
  :(
  
     <div>
       <h1>Create a Meeting</h1>
        <input type='text' placeholder='Room name' value={roomName} onChange={e => setRoomName(e.target.value)} />
        <input type='text' placeholder='Your name' value={displayName} onChange={e => setDisplayName(e.target.value)} />
        <input type='text' placeholder='password' value={password} onChange={e => setPassword(e.target.value)} />
        <button onClick={() => setOnConference(true)}> Let&apos;s start!</button>
     </div>
   )
  
  }
  

export default VideoConference;

i want to start direct videoconference without prejoin screen .any workaround ?
thanks and best regards

I ran your code and it worked for me. I made a few very minor changes that I think you may have overlooked:

  const [displayName, setDisplayName] = useState('my_name')  //<---
  const [roomName, setRoomName] = useState('test_12345678_abcd') //<-- 
  const [password, setPassword] = useState('test_12345') //<---
  ...
  const options = {
     ...
     parentNode: document.querySelector(jitsiContainer),
     ...
  }

If roomName is empty then you will not see the prejoin screen but you will see the “start a new meeting” screen.

Also, regarding:

This cannot be done unless you are hosting your own server. See: