Module 24 — Images in Next.js
Images are an important part of modern Next.js applications. A poorly optimized image can increase page size, slow down Largest Contentful Paint (LCP), consume bandwidth, and negatively affect the user experience.
Next.js provides the next/image component to make image handling easier.
Instead of using:
1<img src="/course.jpg" alt="Next.js Course" />
you can use:
1import Image from "next/image"; 2 3<Image 4 src="/course.jpg" 5 alt="Next.js Course" 6 width={800} 7 height={450} 8/>
The goal is not simply to display an image.
The goal is:
1Image 2 ↓ 3Optimization 4 ↓ 5Responsive delivery 6 ↓ 7Correct dimensions 8 ↓ 9Accessibility 10 ↓ 11Better performance
Why Use next/image?
A normal HTML image:
1<img 2 src="/course.jpg" 3 alt="Next.js Course" 4/>
works, but you are responsible for many optimization concerns.
With next/image:
1<Image 2 src="/course.jpg" 3 alt="Next.js Course" 4 width={800} 5 height={450} 6/>
Next.js can help with:
- Image optimization
- Responsive image delivery
- Lazy loading
- Preventing layout shifts
- Modern image formats
- Image sizing
- Remote image handling
This makes next/image a better default for most application images.
Importing Image
Import the component from next/image:
1import Image from "next/image";
Then:
1<Image 2 src="/images/course.jpg" 3 alt="Next.js Course" 4 width={800} 5 height={450} 6/>
The basic structure is:
1Image 2├── src 3├── alt 4├── width 5└── height
Local Images
Suppose your project contains:
1public/ 2└── images/ 3 ├── nextjs-course.jpg 4 ├── react-course.jpg 5 └── typescript-course.jpg
You can reference them using:
1<Image 2 src="/images/nextjs-course.jpg" 3 alt="Next.js Course" 4 width={800} 5 height={450} 6/>
Remember that files inside public are served from the root URL.
Therefore:
1public/images/course.jpg
becomes:
1/images/course.jpg
Width and Height
For fixed-size images, provide width and height.
1<Image 2 src="/images/course.jpg" 3 alt="Next.js Course" 4 width={800} 5 height={450} 6/>
The aspect ratio is:
1800 / 450
which is approximately:
116 / 9
The dimensions help the browser reserve the appropriate space before the image loads.
This helps reduce Cumulative Layout Shift (CLS).
Responsive Images
Images should usually adapt to the available screen width.
For example:
1<Image 2 src="/images/course.jpg" 3 alt="Next.js Course" 4 width={1200} 5 height={675} 6 className="h-auto w-full rounded-xl" 7/>
The important Tailwind utilities are:
1w-full 2h-auto
Conceptually:
1Desktop 2┌───────────────────────────────┐ 3│ │ 4│ Course Image │ 5│ │ 6└───────────────────────────────┘ 7 8Mobile 9┌─────────────────┐ 10│ │ 11│ Course Image │ 12│ │ 13└─────────────────┘
The image can scale with its container while preserving its aspect ratio.
fill
The fill property is useful when an image should fill its parent container.
For example:
1<div className="relative aspect-video overflow-hidden rounded-xl"> 2 <Image 3 src="/images/course.jpg" 4 alt="Next.js Course" 5 fill 6 className="object-cover" 7 /> 8</div>
Notice the important parent:
1relative
The parent should establish the positioning context for the absolutely positioned image.
Architecture:
1Container 2relative 3 ↓ 4Image 5fill 6 ↓ 7object-cover
object-cover
When using fill, you will frequently use:
1className="object-cover"
Example:
1<div className="relative aspect-video"> 2 <Image 3 src="/images/course.jpg" 4 alt="Next.js Course" 5 fill 6 className="object-cover" 7 /> 8</div>
object-cover makes the image cover the available area while preserving its aspect ratio.
Some portions of the image may be cropped.
object-contain
If you need the entire image to remain visible:
1<Image 2 src="/images/diagram.png" 3 alt="Next.js architecture diagram" 4 fill 5 className="object-contain" 6/>
Conceptually:
1object-cover 2 ↓ 3Fill container 4 ↓ 5Possible cropping 6 7object-contain 8 ↓ 9Show entire image 10 ↓ 11Possible empty space
Use cover for photos and thumbnails.
Use contain for diagrams, logos, screenshots, and illustrations when the entire image matters.
The sizes Property
When using responsive images, sizes tells the browser how much space the image is expected to occupy at different viewport widths.
Example:
1<Image 2 src="/images/course.jpg" 3 alt="Next.js Course" 4 fill 5 sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" 6 className="object-cover" 7/>
This tells the browser approximately:
1Mobile 2100% viewport width 3 4Tablet 550% viewport width 6 7Large desktop 833% viewport width
This allows the browser to select a more appropriate image resource.
Why sizes Matters
Imagine a course card grid:
1Desktop 2 3┌──────────┐ ┌──────────┐ ┌──────────┐ 4│ Course │ │ Course │ │ Course │ 5│ Image │ │ Image │ │ Image │ 6└──────────┘ └──────────┘ └──────────┘
Each image may only occupy about one-third of the page.
If you tell the browser:
1sizes="33vw"
it has better information for selecting an appropriate image size.
Without an accurate sizes value, the browser may download an image that is larger than necessary.
Course Card Image
A practical course card:
1import Image from "next/image"; 2 3export function CourseCard() { 4 return ( 5 <article className="overflow-hidden rounded-xl border bg-white"> 6 <div className="relative aspect-video"> 7 <Image 8 src="/images/nextjs-course.jpg" 9 alt="Next.js course" 10 fill 11 sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" 12 className="object-cover" 13 /> 14 </div> 15 16 <div className="p-5"> 17 <h2 className="text-xl font-semibold"> 18 Next.js Masterclass 19 </h2> 20 21 <p className="mt-2 text-sm text-muted-foreground"> 22 Learn modern Next.js architecture. 23 </p> 24 </div> 25 </article> 26 ); 27}
This combines:
1Course Card 2 ↓ 3Responsive container 4 ↓ 5next/image 6 ↓ 7fill 8 ↓ 9sizes 10 ↓ 11object-cover
Image Priority
Not every image should load with the same priority.
Consider a page:
1Hero Image 2 ↓ 3Important 4 5Course Cards 6 ↓ 7Below the fold 8 ↓ 9Less important
For an important above-the-fold image, you can use the appropriate priority/loading configuration supported by your current Next.js version.
For example:
1<Image 2 src="/images/hero.jpg" 3 alt="Learn Next.js" 4 width={1600} 5 height={900} 6 priority 7/>
Use this carefully.
Do not mark every image as high priority.
A typical strategy is:
1Hero / important LCP image 2 ↓ 3High loading priority 4 5Below-the-fold images 6 ↓ 7Default lazy loading
Hero Image
A hero section might look like:
1<section className="relative min-h-[500px] overflow-hidden"> 2 <Image 3 src="/images/hero.jpg" 4 alt="Developers building modern web applications" 5 fill 6 priority 7 sizes="100vw" 8 className="object-cover" 9 /> 10 11 <div className="relative z-10 mx-auto max-w-7xl px-6 py-32 text-white"> 12 <h1 className="text-4xl font-bold sm:text-6xl"> 13 Learn Modern Web Development 14 </h1> 15 16 <p className="mt-6 max-w-2xl text-lg"> 17 Master Next.js, TypeScript, APIs, 18 databases, and production architecture. 19 </p> 20 </div> 21</section>
Architecture:
1Hero 2├── Background Image 3│ └── next/image 4│ 5└── Content 6 ├── Heading 7 └── Description
Remote Images
Images frequently come from external services.
For example:
1https://images.example.com/course.jpg
You cannot always use arbitrary remote URLs without configuring the image host.
You should configure the allowed remote image source according to the Next.js version and configuration approach used by your project.
Conceptually:
1Next.js 2 ↓ 3Remote Image 4 ↓ 5Allowed Image Host 6 ↓ 7next/image
For example, if your application uses an external image service:
1cdn.example.com
configure that host explicitly rather than allowing arbitrary remote domains.
This provides better security and predictable image behavior.
Remote Image Example
Once the remote host is configured, you can use:
1<Image 2 src="https://cdn.example.com/courses/nextjs.jpg" 3 alt="Next.js Course" 4 width={1200} 5 height={675} 6/>
The important part is:
1Remote URL 2 ↓ 3Configured host 4 ↓ 5next/image
User Avatars
User avatars are usually small but appear throughout an application.
1<div className="relative h-10 w-10 overflow-hidden rounded-full"> 2 <Image 3 src="/images/avatar.jpg" 4 alt="Ankit's profile" 5 fill 6 sizes="40px" 7 className="object-cover" 8 /> 9</div>
For a circular avatar:
1relative 2overflow-hidden 3rounded-full 4fill 5object-cover
This creates:
1 ______ 2 / \ 3 | Avatar | 4 \________/
Avatar Accessibility
Do not use:
1alt=""
for meaningful profile images when the person's identity is relevant.
Prefer:
1<Image 2 src="/images/avatar.jpg" 3 alt="Ankit's profile picture" 4 width={40} 5 height={40} 6/>
For a purely decorative image, an empty alt may be appropriate.
The important question is:
Does the image communicate information to the user?
If yes, describe it.
If no, treat it as decorative.
Alt Text
Good:
1<Image 2 src="/images/nextjs-course.jpg" 3 alt="Next.js course dashboard" 4 width={800} 5 height={450} 6/>
Bad:
1<Image 2 src="/images/nextjs-course.jpg" 3 alt="image" 4/>
Also avoid unnecessary keyword stuffing:
1alt="Next.js course Next.js tutorial Next.js training Next.js course"
Alt text should describe the actual image.
Decorative Images
If an image provides no meaningful information:
1<Image 2 src="/images/decorative-pattern.svg" 3 alt="" 4 width={400} 5 height={200} 6/>
This tells assistive technologies that the image does not need to be announced.
Blog Images
A blog article can use:
1<article> 2 <h1 className="text-4xl font-bold"> 3 Understanding Server Components 4 </h1> 5 6 <Image 7 src="/images/server-components.jpg" 8 alt="Diagram showing Next.js Server Components" 9 width={1200} 10 height={675} 11 className="mt-8 rounded-xl" 12 /> 13 14 <div className="mt-8"> 15 Article content... 16 </div> 17</article>
For article content, accurate dimensions and meaningful alt text are important.
Documentation Diagrams
Documentation often contains diagrams.
For example:
1<div className="relative mx-auto aspect-video w-full max-w-5xl"> 2 <Image 3 src="/images/nextjs-architecture.png" 4 alt="Next.js application architecture showing the relationship between Server Components, Client Components, APIs, and the database" 5 fill 6 sizes="(max-width: 1024px) 100vw, 1024px" 7 className="object-contain" 8 /> 9</div>
Here object-contain is preferable because the entire diagram should remain visible.
Image Performance
Large images are one of the easiest ways to make a web page unnecessarily heavy.
Imagine:
1Original image 25 MB 3 ↓ 4Browser downloads 55 MB 6 ↓ 7Slow page
A properly optimized image can significantly reduce the amount of data transferred.
Think about:
1Image dimensions 2Image format 3Compression 4Responsive sizing 5Lazy loading 6Caching
Avoid Oversized Source Images
Suppose a course card is displayed at:
1400 × 225
but the original source is:
16000 × 3375
You should avoid blindly delivering huge images when smaller resources are sufficient.
The image pipeline should match:
1Required display size 2 ↓ 3Appropriate image resource 4 ↓ 5Browser
rather than:
1Huge source 2 ↓ 3Every device
Image Loading Strategy
Think about image loading in three categories:
1Above the fold 2 ↓ 3Important 4 5Near the viewport 6 ↓ 7Normal 8 9Far below the viewport 10 ↓ 11Lazy
For example:
1Hero image 2 ↓ 3Important 4 5First course row 6 ↓ 7Normal 8 9Footer images 10 ↓ 11Lazy
Avoid aggressively prioritizing every image.
Image Configuration
Your Next.js image configuration is important when working with:
1Remote images 2CDNs 3External image services 4Custom image hosts
The principle is:
1Only allow trusted image sources
For example:
1Application 2 ↓ 3next/image 4 ↓ 5Approved image host 6 ↓ 7Image CDN
Do not design your configuration around arbitrary external URLs unless there is a specific and trusted use case.
Images and Core Web Vitals
Images can affect several important performance metrics.
LCP
A large hero image can become the Largest Contentful Paint element.
1Large Hero Image 2 ↓ 3Slow download 4 ↓ 5Slow LCP
Therefore, important above-the-fold images should be carefully optimized.
CLS
Incorrectly sized images can cause content to move:
1Before image loads 2┌────────────────┐ 3│ Text │ 4└────────────────┘ 5 6Image loads 7 8┌────────────────┐ 9│ Image │ 10├────────────────┤ 11│ Text │ 12└────────────────┘
Providing proper dimensions or using a correctly sized container helps prevent unexpected layout movement.
Responsive Course Gallery
A complete course gallery:
1import Image from "next/image"; 2 3const courses = [ 4 { 5 title: "Next.js Masterclass", 6 image: "/images/nextjs.jpg", 7 }, 8 { 9 title: "TypeScript Masterclass", 10 image: "/images/typescript.jpg", 11 }, 12 { 13 title: "React Masterclass", 14 image: "/images/react.jpg", 15 }, 16]; 17 18export function CourseGrid() { 19 return ( 20 <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3"> 21 {courses.map((course) => ( 22 <article 23 key={course.title} 24 className="overflow-hidden rounded-xl border" 25 > 26 <div className="relative aspect-video"> 27 <Image 28 src={course.image} 29 alt={course.title} 30 fill 31 sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" 32 className="object-cover" 33 /> 34 </div> 35 36 <div className="p-5"> 37 <h2 className="font-semibold"> 38 {course.title} 39 </h2> 40 </div> 41 </article> 42 ))} 43 </div> 44 ); 45}
The architecture is:
1Course Data 2 ↓ 3Course Grid 4 ↓ 5Course Card 6 ↓ 7Image Container 8 ↓ 9next/image
Image Component
For larger applications, you can create a reusable image component.
1import Image, { 2 type ImageProps, 3} from "next/image"; 4 5type CourseImageProps = Omit< 6 ImageProps, 7 "alt" 8> & { 9 alt: string; 10}; 11 12export function CourseImage({ 13 alt, 14 ...props 15}: CourseImageProps) { 16 return ( 17 <Image 18 alt={alt} 19 className="object-cover" 20 {...props} 21 /> 22 ); 23}
Then:
1<CourseImage 2 src="/images/course.jpg" 3 alt="Next.js course" 4 width={800} 5 height={450} 6/>
This allows you to centralize common image behavior.
Image Architecture
A scalable image architecture can look like:
1public/ 2└── images/ 3 ├── courses/ 4 │ ├── nextjs.jpg 5 │ ├── react.jpg 6 │ └── typescript.jpg 7 │ 8 ├── avatars/ 9 │ └── default.jpg 10 │ 11 ├── blog/ 12 │ └── server-components.jpg 13 │ 14 └── docs/ 15 └── architecture.png
For externally hosted images:
1External CDN 2 ↓ 3Configured remote host 4 ↓ 5next/image 6 ↓ 7Next.js application
Common Mistakes
Mistake 1 — Using <img> Everywhere
For most Next.js application images, prefer:
1<Image />
rather than manually using:
1<img />
when the optimization features of next/image are useful.
Mistake 2 — Forgetting alt
Bad:
1<Image 2 src="/course.jpg" 3 width={800} 4 height={450} 5/>
Good:
1<Image 2 src="/course.jpg" 3 alt="Next.js course" 4 width={800} 5 height={450} 6/>
Mistake 3 — Using fill Without a Proper Parent
Bad:
1<div> 2 <Image 3 src="/course.jpg" 4 alt="Course" 5 fill 6 /> 7</div>
Prefer a positioned container with a defined size:
1<div className="relative aspect-video"> 2 <Image 3 src="/course.jpg" 4 alt="Course" 5 fill 6 className="object-cover" 7 /> 8</div>
Mistake 4 — Forgetting sizes
When using a responsive fill image:
1<Image 2 src="/course.jpg" 3 alt="Course" 4 fill 5 className="object-cover" 6/>
consider providing an accurate:
1sizes="..."
This gives the browser better information about the rendered image width.
Mistake 5 — Prioritizing Everything
Avoid:
1<Image priority ... /> 2<Image priority ... /> 3<Image priority ... /> 4<Image priority ... />
Reserve high loading priority for genuinely important images, particularly above-the-fold content.
Mistake 6 — Poor Alt Text
Avoid:
1alt="photo"
Prefer:
1alt="Student viewing a Next.js course dashboard"
when that description accurately represents the image.
Real-Life Application
Build an image system for your learning platform:
1Course Platform 2│ 3├── Hero 4│ └── Hero Image 5│ 6├── Course Listing 7│ ├── Course Thumbnail 8│ ├── Course Thumbnail 9│ └── Course Thumbnail 10│ 11├── Course Page 12│ └── Course Cover 13│ 14├── User Profile 15│ └── Avatar 16│ 17├── Blog 18│ └── Article Images 19│ 20└── Documentation 21 └── Architecture Diagrams
Each image should answer:
1What is this image? 2 ↓ 3What size will it render? 4 ↓ 5Is it above the fold? 6 ↓ 7Is it local or remote? 8 ↓ 9Does it need cropping? 10 ↓ 11What should screen readers hear?
Module 24 Learning Checklist
After completing this module, you should understand:
next/image- Local images
- Remote images
- Image optimization
- Width and height
- Responsive images
fillsizesobject-coverobject-contain- Priority loading
- Lazy loading
- Image configuration
- Remote image hosts
- Image accessibility
- Alt text
- Decorative images
- Course thumbnails
- User avatars
- Hero images
- Blog images
- Documentation diagrams
- Image performance
- LCP
- CLS
- Responsive image architecture
- Reusable image components
Final Mental Model
The correct way to think about images in Next.js is:
1Image Source 2 ↓ 3Local / Remote 4 ↓ 5next/image 6 ↓ 7Dimensions / fill 8 ↓ 9sizes 10 ↓ 11Responsive Delivery 12 ↓ 13Loading Strategy 14 ↓ 15Accessibility 16 ↓ 17Performance
For a production application:
1 Images 2 │ 3 ┌───────────┴───────────┐ 4 ↓ ↓ 5 Local Remote 6 │ │ 7 └───────────┬───────────┘ 8 ↓ 9 next/image 10 ↓ 11 ┌──────────┼──────────┐ 12 ↓ ↓ ↓ 13 sizes fill width/height 14 │ │ │ 15 └──────────┼──────────┘ 16 ↓ 17 Responsive Image 18 ↓ 19 Accessibility + SEO 20 ↓ 21 Performance
The key lesson is:
Images are not just visual assets. In a production Next.js application, image dimensions, loading strategy, responsive sizing, accessibility, and optimization are all part of application performance.