Video Streaming UI
Part of the Frontend Developer Roadmap
In short
Netflix-style rows, a hover preview and a custom player. This project is all about performance: dozens of images, heavy video, and a UI that still scrolls smoothly on a budget phone.
A Netflix/YouTube-style interface — video grid, custom player controls, and category browsing — strong practice for media-heavy, performance-sensitive UI.

Think of it like this
Flipping channels on the family TV
Everyone wants to see what's on before committing, nobody wants to wait, and Dadi needs big, simple buttons. Your streaming UI has the same audience: quick previews, instant start, easy controls.
Watch it happen
Custom controls talking to the video
click play/pause
1/6The viewer clicks play. toggle() checks ref.current.paused.
The big ideas
What you need to know
Custom player controls
Play, seek, volume, speed and fullscreen built on the video element. Proves you know browser media APIs.
Fast thumbnail rows
Lazy-loaded, correctly sized images in horizontal rows. Shows performance awareness.
Keyboard shortcuts
Space to pause, arrows to seek, F for fullscreen, like real players.
Streaming formats
Long videos usually stream as HLS. A library like hls.js plays it in browsers without native support.
How it works
Build plan
- 1
Home page rows
Categories with horizontally scrolling thumbnails.
- 2
Watch page
A video element with a sample video URL.
- 3
Custom controls
Hide default controls and build your own bar.
- 4
Continue watching
Save progress per video and resume from there.
- 5
Performance pass
Lighthouse check, lazy images, responsive sizes.
See it in code
Walk through the code
1function Player({ src }) {2 const ref = useRef(null);3 const [progress, setProgress] = useState(0);4 5 const toggle = () => (ref.current.paused ? ref.current.play() : ref.current.pause());6 const onTime = () => setProgress(ref.current.currentTime / ref.current.duration);7 const seek = (e) => { ref.current.currentTime = e.target.value * ref.current.duration; };8 9 return (10 <div>11 <video ref={ref} src={src} onTimeUpdate={onTime} onClick={toggle} />12 <button onClick={toggle} aria-label="Play or pause">Play / Pause</button>13 <input type="range" min="0" max="1" step="0.001" value={progress} onChange={seek} aria-label="Seek" />14 </div>15 );16}1/4A ref to the real video element, plus progress from 0 to 1.
The video element does the heavy lifting. You read currentTime and duration for progress, and set currentTime to seek. Add aria-labels so icon buttons make sense to screen readers.
Save yourself the pain
Common mistakes
Oops
Expecting autoplay with sound to work on page load.
Do this instead
Browsers usually block autoplay with sound. Start muted, or wait for the user to press play.
Oops
Loading full-size posters for tiny thumbnails.
Do this instead
Serve small images with srcset or next/image, and lazy-load rows below the fold.
Your mission
Milestone 1: your own play button
Quick check
Your video won't autoplay on page load in Chrome, but it plays when you click. What's the usual fix?
Go deeper
- 1Build custom video player controls (play, seek, volume, fullscreen).
- 2Implement lazy-loaded thumbnail grids with hover-preview behavior.
- 3Pay close attention to performance — this project type is easy to make janky.
More projects at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.