YouTube

A React YouTube UI component - the feed card, the Short and the community post. For clones and mockups.

YouTube playground

34:46

Mark Cuban's Biggest Investments EVER | Shark Tank US | Shark Tank Global

2m views1 year ago

The feed grid card - thumbnail badge, verified tick, meta line.

Install YouTube

1. New project? One command installs the package and wires everything - see Installation:

bash
pnpm dlx zyncat-ui init

2. Import the component - it loads its own stylesheet:

tsx
import { YouTube } from '@zyncat/ui/youtube';

Usage

tsx
import { YouTube } from '@zyncat/ui/youtube';
 
<YouTube surface="video" title="Building a design system from zero" channel="Zyncat" views="184K views" age="3 weeks ago" duration="14:22" media={photo} />

Compose YouTube in your application. No Tailwind or external styling library is required; every value resolves from Zyncat UI's token vocabulary.

YouTube props

YouTube

PropTypeDefaultDescription
...htmlAttributesHTMLAttributes—Standard element attributes (aria-*, data-*, id, ...) forwarded to the root.
surfaceYouTubeSurface'video'Which YouTube surface to reproduce - a feed grid card, a Shorts player, or a community post.
titlestring—Video title on `video`, Shorts title on `short`. Clamps to two lines on `video`, one ellipsised line on `short`.
channelstring—The account's display name - channel name on `video`, `@handle` on `short`, author name on `post`.
viewsstring—View count line on `video`, already formatted by the consumer ("2m views").
agestring—Relative timestamp - "1 year ago" on `video`, "9 days ago" on `post`.
durationstring—Runtime shown in the thumbnail badge on `video` ("34:46"). Omit to drop the badge.
verifiedbooleanfalseRenders the grey verified tick after the channel name on `video`.
likesnumber0Like count. Abbreviated the way YouTube abbreviates it - 187000 renders as "187k".
commentsnumber0Comment count. Rendered exact with thousands separators - 3539 renders as "3,539".
remixesnumber0Remix count in the Shorts rail.
pausedboolean—Whether the Shorts overlay shows the play glyph. Controlled; pair with `onPausedChange`.
defaultPausedbooleantrueInitial Shorts play state when `paused` is omitted.
onPausedChange(paused: boolean) => void—Fires when the Shorts play control is pressed. The component never touches the media element itself.
progressnumber0Shorts playback position, 0-100. Consumer-driven: the bar renders where you put it.
textstring—Community post body copy.
carouselYouTubeMedia[]—Community post images. Two or more turn the frame into a paged, draggable, arrow-keyed carousel.
pagenumber—Controlled carousel page index. Omit to stay uncontrolled.
defaultPagenumber0Initial carousel page when uncontrolled.
onPageChange(page: number) => void—Fires whenever the carousel settles on a new page.
mediaYouTubeMedia—Thumbnail on `video`, player content on `short`, single image on `post`. A URL string or your own node (`<img>`, `<video>`, `next/image`). Nothing renders a CSS-only placeholder.
avatarYouTubeMedia—Channel avatar. A URL string or your own node. Nothing renders a flat grey disc.
likedboolean—Controlled like state for the short's heart and the post's thumb-up. The displayed like count adds one while this is on.
defaultLikedbooleanfalseUncontrolled initial like state.
onLikedChange(liked: boolean) => void—Fires when the like toggles.
dislikedboolean—Post only. Controlled dislike state for the thumb-down.
defaultDislikedbooleanfalseUncontrolled initial dislike state.
onDislikedChange(disliked: boolean) => void—Fires when the dislike toggles.
onAction(action: YouTubeAction) => void—Fires for the actions that carry no state: comment, share, remix, menu, expand.
classNamestring—Extra class(es) merged onto the root.
styleCSSProperties—Inline styles merged onto the root.

Frequently asked questions

Import YouTube from '@zyncat/ui/youtube' and pick a surface: <YouTube surface="video" title="Building a design system from zero" channel="Zyncat" views="184K views" age="3 weeks ago" duration="14:22" media={thumb} verified />. surface takes video, short or post, and one prop set spans all three - title, channel, views, age, duration, verified, likes, comments, remixes and text. Each instance is a single card, so the grid in a YouTube clone project is your own array mapped over it, and the data, routing and auth stay yours.

No. There is no YouTube API, no Google login, no network request and no real videos in it - it never fetches and it never plays. media and avatar take a URL string or your own node, so the thumbnail is exactly what you hand it. On the Shorts surface the play control only flips its own glyph and fires onPausedChange, and progress is a 0-100 number you drive; the component never touches a media element and runs no timer.

You do not - it is not an embed wrapper, and it does not play YouTube content. Playing a real video still needs the YouTube iframe player, whether that is an <iframe src="https://www.youtube.com/embed/ID"> of your own or a package like react-youtube. This component draws the interface around the video instead, and media accepts your node, so passing that iframe or a <video> as media puts something playing inside the replica frame.

Three video UI surfaces, behind one surface prop. video is the feed grid card - a 533px column with a 16:9 thumbnail, the duration badge, the 36px channel avatar, a two-line title and the verified tick. short is the Shorts watch page around a 477px 9:16 stage, with the play control, the cc and expand pill, the progress track and the like, comment, share and remix rail - the whole Shorts overlay at its real metrics. post is the community post, a 638px card whose 508px square frame becomes a draggable carousel once carousel gets an array of images.

Yes - a mockup is what a replica is for. Searching "youtube mockup" mostly returns PSDs, Figma community files and vector packs; this draws a live YouTube card in the DOM at the platform's own metrics, so it stays sharp at any zoom and never ages out of date the way a downloaded template or an old screenshot does. It is a React component, not a fake-channel image generator: nothing is submitted anywhere, and every value on screen is a prop you pass.

Yes. @zyncat/ui/youtube ships compiled ESM with its 'use client' directive intact, so it drops into the Next.js App Router with no transpilePackages config and zero runtime dependencies. Being a replica, its metrics are pinned constants your theme cannot move - only the type reads --font-body - and under prefers-reduced-motion the carousel and the Shorts progress bar snap into place instead of easing.

Set in Geist & Newsreader — animated by the house engineZyncat UI · Rev 0.11 · MIT · Built by Tabsir Ahammed · Source on GitHub