# General Customizations

Customize call layouts with custom video placeholder and UI components for `ParticipantView`. Use the SDK's `ParticipantView` component - it handles video rendering, quality updates, subscriptions, and visibility state automatically.

## Best Practices

- Use `ParticipantViewUI` prop to customize the overlay (name, mute states, etc.).
- Use `VideoPlaceholder` prop to customize what shows when video is off.
- Always accept and pass the `style` prop in custom `VideoPlaceholder` for proper positioning.
- Access participant data via `useParticipantViewContext()` hook inside custom UI components.

## ParticipantViewUI and VideoPlaceholder templates

`ParticipantView` comes by default with certain participant information displayed in the "box" - such as mute states of either video or audio, quality indicator, participant's name, reactions, etc. which are displayed over the video element positioned absolutely (see picture). By using the `ParticipantViewUI` property, we can extend the `ParticipantView` component with custom UI elements specific to each participant. This allows us to tailor the visual representation of the participant based on our application's requirements and design.

![ParticipantViewUI elements](https://getstream.io/docs-assets/images/c2adf2bf6fbc.png)

The `VideoPlaceholder` property allows us to replace the default placeholder displayed over the video element when the video is not playing. This placeholder is positioned absolutely over the video element. It provides us with the flexibility to customize the placeholder's appearance and content according to our needs.

>
> **Warning:** Our custom `VideoPlaceholder` component should be able to accept a `style` property coming from `ParticipantView` to allow for absolute positioning.
>

## Customization in SDK provided layout components (preferred way)

We recommend using layout components provided by the SDK as it is possible to modify the `ParticipantViewUI` in these call layouts. You can follow [this guide](https://getstream.io/video/docs/react/v2/ui-cookbook/video-layout/) to build your own call layout.

### PaginatedGridLayout

Call layout for large calls where `ParticipantView` elements are equally sized, you have the ability to customize the `ParticipantViewUI` and `VideoPlaceholder` components using their respective properties of this component.

```tsx
import {
  PaginatedGridLayout,
  StreamVideo,
  StreamCall,
  useParticipantViewContext,
  type VideoPlaceholderProps,
} from "@stream-io/video-react-sdk";

const CustomParticipantViewUI = () => {
  const { participant } = useParticipantViewContext();

  return (
    <div className="participant-name">
      {participant.name || participant.userId}
    </div>
  );
};

const CustomVideoPlaceholder = ({ style }: VideoPlaceholderProps) => {
  const { participant } = useParticipantViewContext();

  return (
    <div className="video-placeholder" style={style}>
      <img src={participant.image} alt={participant.userId} />
    </div>
  );
};

const App = ({ client, call }) => {
  return (
    <StreamVideo client={client}>
      <StreamCall call={call}>
        <PaginatedGridLayout
          VideoPlaceholder={CustomVideoPlaceholder}
          ParticipantViewUI={CustomParticipantViewUI}
        />
      </StreamCall>
    </StreamVideo>
  );
};
```

### SpeakerLayout

The `SpeakerLayout` component allows you to tweak participant UI in the spotlight and in the scrollable bar independently.

```tsx
import {
  SpeakerLayout,
  StreamVideo,
  StreamCall,
  useParticipantViewContext,
  type VideoPlaceholderProps,
} from "@stream-io/video-react-sdk";

const CustomParticipantViewUIBar = () => {
  const { participant } = useParticipantViewContext();

  return (
    <div className="bar-participant-name">
      {participant.name || participant.userId}
    </div>
  );
};

const CustomParticipantViewUISpotlight = () => {
  const { participant } = useParticipantViewContext();

  return (
    <div className="spotlight-participant-name">
      {participant.name || participant.userId}
    </div>
  );
};

const CustomVideoPlaceholder = ({ style }: VideoPlaceholderProps) => {
  const { participant } = useParticipantViewContext();

  return (
    <div className="video-placeholder" style={style}>
      <img src={participant.image} alt={participant.userId} />
    </div>
  );
};

const App = ({ client, call }) => {
  return (
    <StreamVideo client={client}>
      <StreamCall call={call}>
        <SpeakerLayout
          VideoPlaceholder={CustomVideoPlaceholder}
          ParticipantViewUIBar={CustomParticipantViewUIBar}
          ParticipantViewUISpotlight={CustomParticipantViewUISpotlight}
        />
      </StreamCall>
    </StreamVideo>
  );
};
```

### Non-mirrored video

By default, local participant video (video feed from the user's own camera) is mirrored, since people are generally more accustomed to seeing themselves that way. You can disable this behavior using the `mirrorLocalParticipantVideo` prop, which is supported by all built-in layout components:

```tsx
<SpeakerLayout mirrorLocalParticipantVideo={false} />
// or
<PaginatedGridLayout mirrorLocalParticipantVideo={false} />
```

## Standalone ParticipantView customization (in custom call layouts)

The `ParticipantViewUI` property accepts three possible values:

- `ReactElement` - find out what's considered a ReactElement in the [React documentation](https://react.dev/reference/react/isValidElement)
- component which can access `ParticipantView` related data through `useParticipantViewContext` hook
- `null` if you wish to not render any UI

The following example shows the utilization of the `ParticipantViewUI` prop by passing down `ReactElement`:

```tsx
import { type PropsWithChildren } from "react";
import {
  type VideoPlaceholderProps,
  ParticipantView,
  useCallStateHooks,
  useParticipantViewContext,
} from "@stream-io/video-react-sdk";

const CustomVideoPlaceholder = ({ style }: VideoPlaceholderProps) => {
  const { participant } = useParticipantViewContext();

  return (
    <div className="video-placeholder" style={style}>
      <img src={participant.image} alt={participant.userId} />
    </div>
  );
};

const CustomParticipantViewUI = ({ children }: PropsWithChildren) => {
  return <div className="participant-name">{children}</div>;
};

const CustomCallLayout = () => {
  const { useParticipants } = useCallStateHooks();
  const participants = useParticipants();

  return (
    <div className="custom-call-layout">
      {participants.map((participant) => (
        <ParticipantView
          key={participant.sessionId}
          VideoPlaceholder={CustomVideoPlaceholder}
          ParticipantViewUI={
            <CustomParticipantViewUI>
              {participant.name || participant.userId}
            </CustomParticipantViewUI>
          }
        />
      ))}
    </div>
  );
};
```

---

For the most recent version of this documentation, visit [https://getstream.io/video/docs/react/v2/ui-cookbook/participant-view-customizations/](https://getstream.io/video/docs/react/v2/ui-cookbook/participant-view-customizations/).