I have noticed that one of my previously very popular article about how to make a hero image in Next.js is still getting a lot of attention.
Unfortunately, that article was written when Next.js still used the Page Router. After that, with their new App Router in Next.js 13, the image component was replaced with a new one. With that in mind, I have now created a new article optimized for the new Image component in Next.js 13 and 14, for all of you who have, or want to, migrate to use the Image component.
In This Article
- Next.js 13+ Image Component
- Changes in Next.js 13+ Image Component
- How To Create a Hero Image With Next.js 13+
- Solution Explained
- Choosing a Hero Image
- Summary
Next.js 13+ Image Component
The new image component which you can use in Next.js 13 and 14 has changed slightly from the legacy image component used in earlier versions. Both the legacy and the new one offer similar advantages, which makes it very beneficial to use instead of the regular image HTML element. Both of the components can help you with:
- Resizing images for you so you don't need to use srcset manually
- Automatically lazy loads images when they are scrolled into view, which of course is optional
- Gives you various optimization option, such as quality, priority and custom image sizes
- Supports adding a placeholder to blur images when they are loading
For the blurring, you can check my old article if you wonder more about how it works.
I bat Batman would make a great hero image
Changes in Next.js 13+ Image Component
Next.js offers a migration codemod scripts for migrating to the new image components. The codemods are not bullet-proof, so you should check what has changed in the new image component if you are planning to migrate.
Some of the changes have affected how a hero image can be implemented. Mainly, they have replaced layout property, removed the objectFit prop and removed the absolute positioned span element which was wrapping the img element in the legacy image component.
How To Create a Hero Image With Next.js 13+
The principe of how to implement a hero image is quite simple with the new Image component. I would recommend the following setup.
- Start with a wrapper div with relative position, 100vw width and your desired height
- Insert the Next.js image component with fill and priority props set to true, and a class style with a z-index of -1
- Place whatever content you want after the image wrapper, but within the outer wrapper div
Note: You can optionally wrap the image in a wrapper div if you cannot set a style directly on your image component for some reason. In that case, the wrapper must set the CSS attribute position to "absolute" value.
Following the steps above would give you a component looking something like below. The implementation is using css modules. You can also find the code at CodeSandbox and GitHub.
// HeroImage.jsx
import Image from "next/image";
import styles from "./heroImage.module.css";
const { heroContent, heroImage, heroWrapper } = styles;
const IMAGE_URL = "https://www.perssondennis.com/images/perfect-avocado.webp";
const HeroImage = () => {
return (
<div className={heroWrapper}>
<Image
className={heroImage}
priority
src={IMAGE_URL}
fill
alt="hero image example"
/>
<div className={heroContent}>
<h1>Hero Image</h1>
<p>Next.js 13 hero image example.</p>
</div>
</div>
);
};
export default HeroImage;
// heroImage.module.css
.heroWrapper {
position: relative;
width: 100vw;
height: 50vh;
}
.heroImage {
object-fit: cover;
object-position: center;
z-index: -1;
}
.heroContent {
display: flex;
flex-direction: column;
align-items: center;
}
Note that I did also set the objectFit property to "cover". You decide how you want you image to scale by choosing a suitable value for objectFit attribute.
- contain value for objectFit will keep the aspect ratio, which may not be desired for a hero image
- the cover value will fill up the available area in the parent element by cropping the image where it is necessary
- fill option will stretch the image to fill up the parent element
When you crop the image by using "cover" as a value, you may want to set objectPosition to make sure you keep the parts of the image you want from being cropped out.
Above example could look something like this
Solution Explained
CSS is not always that intuitive, so I'll go through what we did there.
The wrapper div around everything had a relative position, what that does, is to tell that absolute positioned images within that element should be positioned relative to it. Absolute positioned elements are relative to the first parent which is explicitly positioned.
The image component Next.js provides does use an absolute position when we use the fill prop for the image component, so the relative position of the wrapper ensures that the image will grow to fill the wrapper and not the whole viewport.
We also added a negative z-index to the image. That is to ensure that the image will be placed under any other content rendered on the same area as the hero image, which means, all the other content we want to display on top of the hero image.
To the image component, we did pass a priority property. This is to disable lazy loading for hero image, since a hero image should always load as quick as possible.
Rest of code should be self explanatory. If it isn't, let me know about your questions.
React Anti-Patterns and Best Practices - Do's and Don'ts
Dennis Persson ・ Feb 5 '23
Choosing a Hero Image
If you don't have a hero image and don't want to buy one, there's a lot of free ways to get them. You can find links to some royalty free image sources in my list of useful image tools.
That article also lists a lot of useful resources to edit images, both an online Photoshop application and tools for editing, generating and converting images to other format.
Speaking of generating an image. You are probably aware of AIs which can generate images for you. While DALL-E may be the most known one, I prefer Leonardo.Ai du to its high quality and realistic images which suits very well for hero images.
Whatever image you choose, make sure to reduce the size of it to a couple of 100 kB or preferably less, dependent on how high resolution and what detail level you need for your image. You can read about the impact of image size and how to optimize an image in my article about how I improved Lighthouse Performance by adding a hero image.
How To Use MVVM in React Using Hooks and TypeScript
Dennis Persson ・ Apr 16 '23
Summary
- Next.js 13 comes with a new image component which is fairly similar to the old one
- Just as the old image component, it offers image optimizations, lazy loading and blurred placeholders
- The new image component can easily be used as a hero image, but remember to use relative position for the parent element
- Use z-index to control that the hero image stays below other content in the hero area