wide-shoe-67933
02/26/2026, 9:27 AMtall-airline-16756
02/26/2026, 9:27 AMtall-airline-16756
02/26/2026, 9:28 AMLocalVideoTrack preview view).
◦ Do NOT apply mirror mode to the published/composited track or to remote renderers.
◦ Result: local user sees a mirrored self-view, remote viewers see the true orientation.
1. Ensure the outgoing/composited video is non-mirrored
◦ When you composite the camera feed with the virtual background for publishing, publish a non-mirrored final frame.
◦ The published track must be the true (non-flipped) image so remote participants and recording see correct orientation.
1. Handle virtual backgrounds that contain text
◦ Pre-flip (horizontally invert) the background image before compositing if you are compositing a mirrored local preview into the outgoing frame.
◦ Better practice: Composite from the unmirrored camera frame and use an unflipped background image. Then locally mirror only the preview UI. This avoids having to flip backgrounds at all.
◦ If you cannot composite from an unmirrored feed for some reason, pre-flip the background image so when the mirrored camera feed is used it yields a final non-mirrored result for remote viewers.
1. Renderer selection & configuration
◦ Use your platform’s renderer settings to enable mirror only on the local preview (e.g., VideoTextureViewRenderer or SurfaceViewRenderer on Android) and ensure remote renderers are not set to mirror.
◦ Confirm the final frame you publish (the track) is not mirrored.
Short implementation checklist
• [ ] Local preview view: mirror = true
• [ ] Remote participant views: mirror = false
• [ ] Published/composited track: mirror = false
• [ ] Virtual background with text: either composite from an unmirrored camera frame (preferred) or pre-flip the background image before compositing
Questions that will help me give exact code or steps
• Which platform / SDK are you using (Web, iOS, Android, or another)?
• Are you compositing backgrounds client-side before publishing, or are you using a server-side compositor?
• If client-side, which renderer class are you using for the local preview (so I can show the exact property to toggle)?
If you tell me your platform and whether composition is client- or server-side, I’ll give a short code example showing: (1) how to enable mirror only on the local preview and (2) how to composite/publish a non-mirrored track so text in backgrounds remains readable for everyone.
Sources: client-sdk-android | livekit/livekit_composite | DeepWikiwide-shoe-67933
02/26/2026, 10:11 AMtall-airline-16756
02/26/2026, 10:11 AMscale-x-[-1] because that flips the whole DOM element (video + background + overlays). LiveKit provides renderer-level mirroring so you can mirror only the video track (the local self-view) without affecting virtual backgrounds or UI.
What to do
• Stop using scale-x-[-1] on the video tile.
• Use LiveKit’s renderer mirror option for the local preview only, and keep remote renderers and the published/composited track unmirrored.
• If you composite virtual backgrounds client-side, composite from the unmirrored camera frame (preferred). If you must composite from a mirrored frame, pre-flip the background image so text stays readable for remote viewers.
Quick platform-specific notes — which mirror setting to use
• Web (React / @livekit/components-react): use the mirror prop on the video renderer / ParticipantTile so only the video is mirrored instead of the whole element.
◦ Example: <videorenderer ... mirror={true}/> or ParticipantTile with mirror set.
• iOS: set videoRenderer.mirror = true on your VideoTextureViewRenderer / VideoView.
• Android: set videoRenderer.mirror = true on VideoTextureViewRenderer or SurfaceViewRenderer.
• Flutter: set mirrorMode: true on the VideoRenderer widget.
Why this fixes your problem
• Renderer-level mirroring flips only the video buffer. Overlays and background images remain their original orientation, so text in virtual backgrounds isn’t reversed for remote viewers.
• The published/composited track remains non-mirrored so everyone else sees the correct orientation.
One question to give you exact code
• Which platform/SDK are you using (Web React, plain Web, iOS, Android, or Flutter)?
• Are you compositing virtual backgrounds client-side before publishing, or using a server-side compositor?
Tell me those and I’ll post a short code snippet showing exactly where to toggle mirror mode and how to ensure your background text remains readable.
Sources: React Core and Components | client-sdk-android | livekit/livekit_composite | DeepWikiwide-shoe-67933
02/26/2026, 10:14 AMtall-airline-16756
02/26/2026, 10:14 AMVideoTrack and currently using Tailwind’s scale-x-[-1], which flips the entire DOM (video + background). To mirror just the video track (local preview) without affecting overlays or virtual backgrounds, use the built-in mirror prop on the LiveKit VideoTrack component.
If this is React with @livekit/components-react, update your JSX like this:
<VideoTrack
key={track?.publication?.trackSid || uuidv4()}
className={videoClass}
trackRef={track}
mirror={track?.publication?.isLocal} // mirror only local track
/>
Notes:
• Use mirror={true} only for the local participant’s track (example above uses isLocal).
• This applies mirroring at the renderer level so overlays and background elements are not flipped.
• The published/composited track remains unmirrored, so remote participants see the correct orientation.
If you want, tell me:
• whether you’re using @livekit/components-react specifically, and
• how you’re rendering virtual backgrounds (separate overlay element or composited into the outgoing frame),
and I’ll give a tiny tailored snippet showing the local preview + unflipped background setup.
Sources: Bringing AI avatars to voice agents | Bringing AI avatars to voice agents | LiveKit coming to React Native! | LiveKit coming to React Native!wide-shoe-67933
02/26/2026, 10:16 AMVideoTrack component is responsible for rendering participant video tracks like camera and screen_share. This component must have access to the participant's context, or alternatively pass it a Participant as a property.
@example
<VideoTrack trackRef={trackRef} />
@see — @alert-easter-19258/components-react#ParticipantTile | ParticipantTile
_@public_" but with you solution i get this error: Type '{ key: string; className: string; trackRef: TrackReference; mirror: boolean; }' is not assignable to type 'IntrinsicAttributes & VideoTrackProps & RefAttributes<HTMLVideoElement>'.
Property 'mirror' does not exist on type 'IntrinsicAttributes & VideoTrackProps & RefAttributes<HTMLVideoElement>'.ts(2322)tall-airline-16756
02/26/2026, 10:16 AMVideoTrack component, and VideoTrack does not accept a mirror prop, so TypeScript is correctly preventing that prop from being passed.
What I recommend
• Use ParticipantTile (higher-level component) for local self-view mirroring — it supports the mirror prop and applies mirroring at the renderer level so overlays/virtual backgrounds are not flipped.
• Keep VideoTrack for raw track rendering when you don’t need mirroring; if you must use VideoTrack, mirror only the video element (not the whole container) with a targeted style as a fallback.
Concrete options
1) Preferred — switch to ParticipantTile (recommended)
• Replace your VideoTrack usage for the local participant with ParticipantTile and set mirror={true} only for the local participant.
• Example:
import { ParticipantTile } from "@livekit/components-react";
<ParticipantTile
participant={localParticipant} // or pass participant via context
mirror={true} // mirrors only the video renderer
className={videoClass}
/>
• This mirrors the video buffer for the local preview and leaves overlays/virtual backgrounds unflipped. The published/composited track remains non-mirrored for remote viewers.
2) If you must keep VideoTrack (fallback)
• There is no built-in mirror prop on VideoTrack. If you cannot switch to ParticipantTile, apply mirroring only to the video element itself (not the container) so overlays remain intact:
◦ Add a CSS class that targets the actual <video> element inside the VideoTrack component and apply transform: scaleX(-1) there.
◦ Example CSS (scoped):
/* target the video element rendered by VideoTrack */
.my-video .lk-video-element {
transform: scaleX(-1);
}
• You’ll need to inspect the DOM structure (or the class name LiveKit places on the video element) to target only the video element. This is less ideal but workable.
Quick questions so I can give a drop-in snippet
• Are you able to render the local participant via ParticipantTile instead of VideoTrack? (Yes / No)
• Which package version are you using for @livekit/components-react? (I can tailor code if you share the version)
• How are you rendering your virtual background— as a DOM overlay on top of the video, or are you compositing background into the outgoing track?
Reply with those answers and I’ll give the exact JSX/CSS you can paste directly into your code.
Sources: client-sdk-js | livekit/livekit_composite | DeepWiki | LiveKit coming to React Native! | Streaming video from a canvas with WebRTC and React | React Core and Components | React Core and Componentswide-shoe-67933
02/26/2026, 10:21 AM.my-video .lk-video-element {
transform: scaleX(-1);
}
with my VideoTracktall-airline-16756
02/26/2026, 10:21 AMVideoTrack so overlays and backgrounds aren’t flipped.
What we’re doing
• Add a parent className to the VideoTrack container.
• Target the video element LiveKit renders (it uses the class lk-video-element) and apply transform: scaleX(-1) to that element only.
1) JSX: give the VideoTrack a parent class
import { VideoTrack } from "@livekit/components-react";
function ParticipantView({ track }) {
return (
<VideoTrack
trackRef={track}
className="my-video" // parent container class
key={track?.publication?.trackSid}
/>
);
}
2) CSS: mirror only the video element
/* scope to your container so only this video is mirrored */
.my-video .lk-video-element {
transform: scaleX(-1);
/* keep transform origin and smoothing if needed */
transform-origin: center;
backface-visibility: hidden;
}
Notes and tips
• This mirrors only the actual <video> element that LiveKit injects (.lk-video-element), not the container or overlays layered around it.
• Ensure your CSS file is imported (or include the rule in your global stylesheet). If you use CSS modules, adapt the selector to match the generated class names.
• Use this only for the local participant’s tile. For remote participants, omit the .my-video class (or conditionally apply it).
• If you later switch to ParticipantTile, it supports a mirror prop natively and avoids manual CSS.
If you want, tell me:
• how you identify local vs remote tracks in your code, and I’ll show a conditional example that applies the my-video class only for the local participant. Sources: React Core and Components | React Core and Components | React Core and Componentswide-shoe-67933
02/26/2026, 10:30 AMtall-airline-16756
02/26/2026, 10:30 AMVideoTrack in React.
Option A — Use a ref and set the video element’s style when it mounts
• This targets the actual HTMLVideoElement rendered by LiveKit and flips it inline.
import React, { useRef, useEffect } from "react";
import { VideoTrack } from "@livekit/components-react";
function LocalVideo({ track }) {
const videoRef = useRef<HTMLVideoElement | null>(null);
useEffect(() => {
const el = videoRef.current;
if (!el) return;
// apply inline mirroring to only the video element
el.style.transform = "scaleX(-1)";
el.style.transformOrigin = "center";
el.style.backfaceVisibility = "hidden";
// cleanup if the element is removed/replaced
return () => {
if (el) el.style.transform = "";
};
}, [track]);
return (
<VideoTrack
trackRef={track}
key={track?.publication?.trackSid}
// pass the ref to the actual DOM video element via `ref`
ref={videoRef as any}
/>
);
}
Option B — Use the component’s render callback (if available) to style the element
• Some LiveKit components let you access the media element via a callback prop; if your VideoTrack exposes the actual element through an onRenderElement (or similar) prop, use that to set inline styles.
function LocalVideo({ track }) {
const onRenderElement = (el: Element | null) => {
if (el instanceof HTMLVideoElement) {
el.style.transform = "scaleX(-1)";
el.style.transformOrigin = "center";
el.style.backfaceVisibility = "hidden";
}
};
return (
<VideoTrack
trackRef={track}
key={track?.publication?.trackSid}
// hypothetical prop name; if your version supports it, use it
onRenderElement={onRenderElement as any}
/>
);
}
Important notes
• Only apply this for the local participant’s tile. Remote tiles should not be flipped.
• Inline styling like this mirrors only the video element (not the container), preserving overlays/virtual-backgrounds layered outside the video element.
• If your VideoTrack does not accept ref or an element callback, you can wrap it in a container, query for .lk-video-element inside that container in useEffect, and set the inline style on that found element.
If you want, tell me:
• which exact version of @livekit/components-react you’re using, and
• whether VideoTrack accepts ref or an element callback in your build,
and I’ll give the exact drop-in code for your version (or the DOM-query fallback).
Sources: Managing Video Quality During Network Congestion | LiveKit | Client SDKs | livekit/livekit_composite | DeepWiki