Building a video gallery just like in Zoom 🎥

June 01, 2020


I posted a fun part 2, where I am building the same UI using experimental CSS Layout API from CSS Houdini 🎩.

Check it out!


Complete solution is here. Change videos count and resize the screen to see it in action.


Hi folks 👋

I had to build a video gallery view similar to the one ZOOM has for Remeet.

I’ve spent quite a bit of time trying to figure out how to build it with pure CSS and I… FAILED 😕.

Sharing my current solution with

  • a bit of JavaScript
  • CSS custom properties
  • display: flex

If someone has an idea on how to achieve similar result without using JavaScript, please share 🙏


Having videoCount videos with fixed aspectRatio and fixed container size (containerWidth, containerHeight), fit all the videos inside the container to occupy as much area as possible. Videos should have the same size and can’t overflow the container.


Calculating size for a video

First I needed to make sure videos are not overflowing the container and occupying as much area as possible.

function calculateLayout(
  containerWidth: number,
  containerHeight: number,
  videoCount: number,
  aspectRatio: number
): { width: number; height: number; cols: number } {
  // see implementation in codesandbox

Current implementation brute-force searches the layout which occupies the most of the available space. It compares the sum of video areas for every possible number of columns.

// pseudocode, see codesandbox for complete version
let bestArea;
for (let cols = 1; cols <= videoCount; cols++) {
   const currentArea = /* sum of video areas in this layout */
   if (bestArea < currentArea) {
      bestArea = currentArea;

There is also a npm module that does just that! github

But is there a better way then brute-force? Does it worth it if we assume the maximum videoCount is 50? 🤔

Markup, styles & CSS custom properties

The HTML structure I went with:

    <div id="gallery">
      <div class="video-container">
      <div class="video-container">

I applied calculated width and height to .video-container.

.video-container {
  width: var(--width);
  height: var(--height);

I used CSS custom properties to pass values calculated in JavaScript.

const gallery = document.getElementById("gallery")"--width", width + "px")"--height", height + "px")"--cols", cols + "")

⚠️ Don’t forget to recalculate these values when the screen size or number of videos changes.

Then I used display: flex to layout .video-container elements

#gallery {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  max-width: calc(var(--width) * var(--cols));

As .video-container sizes are calculated to fit into the container, I didn’t have to worry about anything else here. I also found justify-content: center; to work best for the use case, as it nicely centers not fully filled rows. The purpose of max-width is to force line breaks.

Handling different aspect ratios

This gallery has a constraint of a fixed aspect ratio for all elements. But videos could have different ratios. This is where having .video-container wrapping <video/> is handy.

.video-container {
  width: var(--width);
  height: var(--height);

video {
  height: 100%;
  width: 100%;

This way video occupies as much space as possible within its container and preserves its original aspect ratio.

For example, changing the ratio of a container from 16:9 to 1:1 doesn’t distort videos with the original 16:9 ratio.

Different aspect ratio


This is how it looks in the real world:

Screenshot from Remeet with grid layout

Check out the complete solution here. Change videos count and resize the screen to see it in action.

Open questions❓

  1. Is it possible to achieve a similar result without calculating video size in JavaScript? 🤔
  2. Is there a better way of calculating video sizes than brute-force search? Is it worth it if a number of videos can’t exceed 50? 🤔

Profile picture

Hi, I'm Anton 👋
I am building Remeet , a tool that keeps meeting fatigue away from dozens of distributed teams across the globe
I work on Kibana at Elastic