> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streampixel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Mounting the stream

> Append the SDK's rootElement to your DOM, configure the video element, and hide the default Pixel Streaming UI.

This page covers how to mount the SDK-managed video into your page, configure the video element, and hide the default Pixel Streaming UI.

## The `rootElement`

`appStream.rootElement` is the DOM element that contains the video player and all SDK UI components. You must append it to your page for the stream to be visible.

## When to mount

Mount the `rootElement` inside the `onVideoInitialized` callback. This ensures the video element is ready before you add it to the DOM:

```javascript theme={"dark"}
appStream.onVideoInitialized = () => {
  document.getElementById('video-container').appendChild(appStream.rootElement);
};
```

## Configuring the video element

After mounting, you may want to configure the video element:

```javascript theme={"dark"}
appStream.onVideoInitialized = () => {
  document.getElementById('video-container').appendChild(appStream.rootElement);

  // Access the video element
  const video = appStream.stream.videoElementParent.querySelector('video');
  if (video) {
    video.muted = true;       // Start muted (recommended for autoplay)
    video.autoplay = true;
    video.tabIndex = 0;       // Make focusable for keyboard input
    video.focus();            // Focus for immediate keyboard capture
  }

  // Mute the separate audio element
  const audio = appStream.stream._webRtcController?.streamController?.audioElement;
  if (audio) {
    audio.muted = true;
  }
};
```

## Hiding the default Pixel Streaming UI

The SDK inherits some default UI elements from Epic Games' Pixel Streaming library. You'll typically want to hide these and build your own:

<Warning>
  The `#uiFeatures` element may re-appear after the video initializes. You must hide it both after SDK initialization and inside the `onVideoInitialized` callback to ensure it stays hidden.
</Warning>

```javascript theme={"dark"}
// Hide after SDK initialization
const uiFeatures = appStream.uiFeaturesElement
  || appStream.rootElement?.querySelector('#uiFeatures');
if (uiFeatures) uiFeatures.style.display = 'none';

// Also hide after mounting (the element may re-appear)
appStream.onVideoInitialized = () => {
  document.getElementById('video-container').appendChild(appStream.rootElement);

  const uiEl = appStream.rootElement?.querySelector('#uiFeatures');
  if (uiEl) uiEl.style.display = 'none';
};
```

You can also use CSS:

```css theme={"dark"}
#uiFeatures {
  display: none !important;
}

#afkOverlay {
  display: none !important;
}
```

## The `onDisconnect` callback

Called when the stream disconnects:

```javascript theme={"dark"}
appStream.onDisconnect = () => {
  // Show your disconnected UI
  document.getElementById('video-container').innerHTML = '';
  showDisconnectedMessage();
};
```

## Full-screen video container

For a full-viewport stream, style your container:

```css theme={"dark"}
body {
  margin: 0;
  overflow: hidden;
}

#video-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
}

/* Ensure the SDK video fills the container */
#video-container video {
  width: 100%;
  height: 100%;
  object-fit: fill;
}
```

## Next steps

<CardGroup cols={2}>
  <Card title="Custom loading screen" icon="spinner" href="/resources/web-sdk/ui-and-customization/custom-loading-screen">
    Build a loading screen driven by SDK lifecycle events.
  </Card>

  <Card title="Overlays and UI components" icon="layer-group" href="/resources/web-sdk/ui-and-customization/overlays-ui-components">
    Replace built-in overlays with your own UI.
  </Card>
</CardGroup>
