How to Style Nextjs Images using the next/image library

December 21, 2021 (3y ago)

/* you need to wrap the component */
.image-wrapper {
  box-sizing: content-box;
}
  /* the magic happens here */
  .image-wrapper > div {
    box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
    border-radius: 5px;
  }
`;
// We need to wrap our images in a parent div
<div className='image-wrapper'>
  <Image alt='...' src='...' width={800} height={400} quality={70} />
</div>

Next.js next/image component offers your site some fantastic benefits straight out of the box. Benefits like lazy loading, optimization, responsiveness and resizing. If you're buliding with Next.js then you'll definitely want to use them.

The only issue is they can be a nightmare to style. I was banging my head against a wall trying to figure out how to style Image component.

You see there are two issues that make it hard to style your next/image component.

The first is that it has inline styles meaning that those styles will overwrite any styles you apply to a selector. You could use the !important property but that comes with its own risks and side effects. I think the solution below is a nicer way to handle this issue.

The second is the image is wrapped in divs meaning certain styles won't function the way you want them too. Depending on the properties you pass an Image it can be wrapped in one or two divs. Let's look at what an next/image looks like in the browser. Below is an outline of their structure, but inspect the browser tools on this image to see for yourself.

<div>
  <img />
</div>

<!--Or-->

<div>
  <div>
    <img />
  </div>
</div>

The divs above if you looked at the example have their own inline styling which causes issues when we try to style the component. If you tired to add border-radius to the img tag you will get a weird shadow effect due to the parent divs.

Thankfully the below solution will help solve both of these issues.

The Solution

To overcome these issues we can wrap the Image component in a wrapper div and give it a class name. The reason we want to do this is so that we can now target the divs that automatically wrap our Image. Above we have done this by wrapping it with the <div className="image-wrapper"> element.

We can now target the children divs by using a child combination selector. In the snippet above we have the class image-wrapper we then use the > symbol to select all children of the type we specify. Therefore in this instance we want to use > div to select all children divs of the div with the image-wrapper class.

Now that we have a wrapper, we can add styles to those divs to have our next/images looking the way we want them to.

All you need to do is add the styles in the below selector.

.image-wrapper > div {
  /*styles go here*/
}

If you wanted to apply styles to the img tag itself you have a few options.

  1. You could target the image like so .image-wrapper > img
  2. You could add a className to the Image component

The key thing to remember is the Image has styles applied inline so you if you want to overwrite any of them you need to use the !important property.

Reusability

What I do on my blog, the one you are reading now is I create my own Image component for blog posts. This component has a div that wraps the next/image, so that the styling is applied across my whole website.

I use styled components to make this happen, if you want to copy it for you website feel free to grab the snippet below.

import Image from "next/image";
import styled from "styled-components";

const ImageWrap = styled.span`
  margin: 32px auto;
  box-sizing: content-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;

  & > div {
    box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
    border-radius: 5px;
  }
`;

const PostImage = ({ alt, src, width, height }) => {
  return (
    <ImageWrap>
      <StyledImg
        alt={alt}
        src={`/images/articleImgs/${src}`}
        width={width}
        height={height}
        quality={70}
      />
    </ImageWrap>
  );
};

export default PostImage;

Update For Next.js 12

Nextjs 12 now uses a span instead of a div. So if you have upgraded from a previous version you want to change your css selector to the pattern below:

const ImageWrap = styled.span`
  // same as before

  // just change this css selector
  
  & > span {
    ...
  }
`;