Resize Images And Maintain Aspect Ratio in React and CSS

In this lesson, we will build the Frame primitive, which frames out a visual media, like an image or video, and forces it into the aspect ratio needed for the design without distorting the image.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

Table of Contents

This lesson preview is part of the Composing Layouts in React course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Composing Layouts in React, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Composing Layouts in React
  • [00:00 - 00:06] What's up guys? Travis here with another lesson in composing layouts in React.

    [00:07 - 00:15] This lesson we are going to learn about the frame component. Images on the web are surprisingly tricky.

    [00:16 - 00:28] A whole course can be devoted to best practices for images on the web. But from a layout point of view, one of the more difficult parts to deal with is the size and aspect ratio.

    [00:29 - 00:43] The images we use are rarely the same size or aspect ratio as the web page design. The problem is compounded by the need to support multiple viewports that our image will need to load.

    [00:44 - 01:01] We typically deal with the size issue by making the image size responsive, which is done by setting the max width of the image to 100%, which is what we did in our CSS reset. However, dealing with aspect ratios is not as simple.

    [01:02 - 01:15] Images have an intrinsic aspect ratio based on the actual width and height of the image file. If we try to force an image into a box of a different aspect ratio, then the image becomes skewed and deformed.

    [01:16 - 01:42] What we need is a way to not just control the size, but also the aspect ratio by cropping the image where it is too tall or too wide for the aspect ratio that we need. In this lesson we will be building the frame primitive, which frames out visual media like in the image or video and forces it into the aspect ratio needed for the design which without distorting the image.

    [01:43 - 01:54] In this lesson we are going to be building a new arrivals card like this. In the above widget, the image is cropped at a one by one aspect ratio or another works a square.

    [01:55 - 02:05] It will also need to be responsive to the size of the parent container by growing or shrinking accordingly. Once again, you can follow along in the link below.

    [02:06 - 02:13] There is a copy to this project. What we are going to do now, we have got this new arrival.

    [02:14 - 02:32] This is where we are going to do all of our work and it is being exported inside of this grid so we can have multiple new arrival objects. This image of the shirt is in this public folder under assets.

    [02:33 - 02:43] There is this details which is where we are getting this aliblouse $35.99 detail right there. You can go look at it if you want.

    [02:44 - 02:51] It is not really important for what we are doing in this course. Let's go ahead and start building our frame.

    [02:52 - 03:13] We are going to import styled from styled components. This is the magic of copying and pastel.

    [03:14 - 03:29] Let's bring that in. I forget to bring in that final backtake.

    [03:30 - 04:06] Let's go ahead and put it around our image and then we will talk about what is going on here. What we have here is this outer div, this frame is being set as a position relative.

    [04:07 - 04:17] For now, hard coding it to 18 rems. This is not a long term solution because as we move around, we want this to be centered in there.

    [04:18 - 04:27] We are going to set this with an height to change. We are going to set it for that way for now.

    [04:28 - 04:40] What we are doing inside here is saying all the direct children, which is this image tag, we are going to set it as position absolute. We are going to set top, bottom, left and right to zero.

    [04:41 - 04:56] That is going to pin the object to all the sides of this outer frame here. We have this width and height.

    [04:57 - 05:07] We are going to set that as 100% and 100% for both. This is especially for image and video tags.

    [05:08 - 05:28] We need that little extra setting here to force the image to live inside of this box. The reason why we don't have to put any units on this is because zero is always zero.

    [05:29 - 05:33] It doesn't matter. It is zero rem, view heights, pixels, whatever.

    [05:34 - 05:36] It is zero. Anything is always zero.

    [05:37 - 05:55] Those are one of those unique things in CSS that you can just put zero and you don't have to put the units. Now we have this put inside this box, but now we have distorted the image to get it in there.

    [05:56 - 06:02] We are going to tackle a couple of different things here. First thing we are going to tackle is setting the aspect ratio.

    [06:03 - 06:25] Instead of hard coding this with an height to be 18 rems, let's just set an aspect ratio and let the browser decide how big, how tall and how wide it should be. The good news is there is a property that is available called aspect ratio.

    [06:26 - 06:36] You can come in here. Let's say you wanted a 16 by 9 aspect ratio and you can set it just like that.

    [06:37 - 06:48] You know me setting this aspect ratio, I am going to get rid of that. This aspect ratio is not working.

    [06:49 - 07:07] That's because we are in Safari. If we look at can I use this aspect ratio is available in Chrome and Edge and as of the day of this recording it is also available in Firefox.

    [07:08 - 07:18] Safari is not yet implemented. There is another way we can handle this.

    [07:19 - 07:36] The best way is we want to use the aspect ratio by default if at all possible. If not we want to fall back to an alternate form.

    [07:37 - 07:56] I am going to copy this and we will talk through how this is working after I put it in here. What we are doing here is first of all we are off of prompts.

    [07:57 - 08:05] We are going to grab a value. We have this prompt called ratio and it is going to be an array.

    [08:06 - 08:20] We are taking the first value and we are assigning it to end which basically means numerator. This other property we are assigning to a custom property called D which is denominator.

    [08:21 - 08:36] If we don't provide that property we are going to default to 1. For those browsers that support aspect ratio we are going to go ahead and pull those values out.

    [08:37 - 08:44] We are going to get the end for numerator and D for denominator. Then we are going to do what is called a feature query.

    [08:45 - 09:16] We can actually go at supports and in this case we are throwing in the not because we want to we are checking if something doesn't support this. This basically is the equivalent of JavaScript's bang operator that allows it to take on the opposite effect.

    [09:17 - 09:21] It is the equivalent of that. We don't have to check these values specifically.

    [09:22 - 09:30] We are just going to check does that work. Basically what happens is this is saying does CSS understand what I am putting in here.

    [09:31 - 09:46] If CSS does understand it or in this case because we are checking for the not if it doesn't understand it then it is going to use this CSS. Then we are doing an interesting thing here.

    [09:47 - 09:55] The padding bottom. This has been a trick, a hack, whatever you want to call it.

    [09:56 - 10:12] It has been around for at least a decade. It takes advantage of the fact that padding, padding that is set on the top or the bottom is always relative to the width of the element itself.

    [10:13 - 10:53] In other words if we set a padding to be 25% it is 25% of the width of the element, not the height. Using that we can calculate, what we need to do to calculate that is we actually flip these numbers if we take the denominator divided by the numerator of an aspect ratio and then multiply that by a percentage we get the percentage we need to get that same aspect ratio.

    [10:54 - 11:06] If you are not familiar with the aspect ratio by the way, we have the numerator and denominator. This corresponds to the width and this corresponds to the height.

    [11:07 - 11:40] If we have 16 pixels by 9 pixels that means for every 16 pixels wide we are going to have 9 pixels tall or that same exact ratio. We can actually have more or less than that 16 pixels but we are going to keep that same amount of ratio of no matter how many pixels wide we are we are going to have a ratio of 16 by 9.

    [11:41 - 12:21] So once again we are getting the numerator, the denominator from our ratio, we are setting the aspect ratio by default and if it does not support it like Safari, then we are going to use this padding bottom technique that will force the height of the element to be a ratio of the width of the element. So now we have to refresh this sometimes we write a lot the hot refresh breaks.

    [12:22 - 12:55] If we come in here to the frame, we are going to set a ratio of 1 to 1. We should fix it and there we go we have that.

    [12:56 - 13:30] And you have to just refresh. And then we are going to pull it up on a new browser.

    [13:31 - 13:52] There we go. So if we wanted to for example have something be 16 pixels wide by 9 pixels tall, you can see that is happening automatically even though aspect ratio is not supported by Safari.

    [13:53 - 14:22] So we are having that falling back and if we were to inspect this element, you are going to see that the padding for that is set. This is the calculation if we look at the computed.

    [14:23 - 14:38] The padding right there is 192 pixels that is on show it right there. 192 pixels which is a relative amount to the pixels wide it is.

    [14:39 - 14:52] So that works great we could the problem is we are still distorting our image unfortunately. Let's put this back to 1 by 1.

    [14:53 - 15:10] So now we need to fix that. And to fix that we are going to need to move this width and height out of here we are going to just put it under a specific category just for images.

    [15:11 - 15:22] And there is a reason why. The reason is we are also going to do this thing called object fit cover.

    [15:23 - 15:43] And this is some magic here. This says we want you to cover this area and any part that is outside of the bounds of the box that we have set crop it.

    [15:44 - 15:59] So we are just going to cover it but don't mess with the aspect ratio the inherit aspect ratio of the image. Now we also want to take advantage of the fact that maybe we don't have an image.

    [16:00 - 16:07] Maybe we have something else. Maybe we have another div that we want to do something similar with.

    [16:08 - 16:24] And so we are going to go ahead and force that item to be display flex, center it both in the just fine line and then we are going to do overflow hidden. And this does the exact same thing as this does.

    [16:25 - 16:34] But it does it for objects that are not images of video. Now if you want to go learn more about object.fit there is a great MDN article.

    [16:35 - 16:39] But honestly this is really all you need to know for the frame components. We are just going to leave it at that.

    [16:40 - 16:58] Just know that this allows an image to maintain its aspect ratio but the browser will crop the part that doesn't fit inside the box that is trying to support. And now there we are.

    [16:59 - 17:14] We have exactly what we need to get this working just the way we want. Now there is actually one little tweak that we want to do.

    [17:15 - 17:32] Right now we are forcing this ratio to be a one to one. But sometimes we don't want to set a ratio itself.

    [17:33 - 17:51] Sometimes we want to allow the environment to set the width and height of this box for us but we still want to maintain the aspect ratio and this cover effect. So what we are going to do one final little tweak here.

    [17:52 - 18:04] And that is we are going to take all this part right here. And we are going to conditionally render this part, this aspect ratio part.

    [18:05 - 18:16] If a ratio is actually provided. And this will give us the freedom to not have a ratio.

    [18:17 - 18:46] And then we could, I am going to hard coat this for now just so you can see the effect. You can set the height to be 100 pixels and the width to be 50 pixels.

    [18:47 - 19:09] And in this case this is not a great example because I don't want to, this isn 't exactly what we wanted here but this was hard coat this to maybe like 500 pixels. You can see 500 by 550.

    [19:10 - 19:31] We can do different things and this will force the aspect ratio of the image underneath to be exactly what we, the same it won't distort it but it will still fit within the square. Let's go back to the one by one ratio though because that's what the design wanted.

    [19:32 - 19:39] Perfect. So the final coat for this lesson is at the end of the notes below.

    [19:40 - 19:42] We've worn out three. We have the pad box, the center, the frame.

    [19:43 - 19:53] We just have one more. And it's a special primitive because it takes, it's kind of a amalgamation, a combination of both a wrapper and a spacer.

    [19:54 - 19:58] And we're going to learn how to make that primitive, the cover primitive in the next