260223:1415 20260223 nextJS & nestJS Best pratices
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
This commit is contained in:
173
.agents/skills/next-best-practices/image.md
Normal file
173
.agents/skills/next-best-practices/image.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Image Optimization
|
||||
|
||||
Use `next/image` for automatic image optimization.
|
||||
|
||||
## Always Use next/image
|
||||
|
||||
```tsx
|
||||
// Bad: Avoid native img
|
||||
<img src="/hero.png" alt="Hero" />
|
||||
|
||||
// Good: Use next/image
|
||||
import Image from 'next/image'
|
||||
<Image src="/hero.png" alt="Hero" width={800} height={400} />
|
||||
```
|
||||
|
||||
## Required Props
|
||||
|
||||
Images need explicit dimensions to prevent layout shift:
|
||||
|
||||
```tsx
|
||||
// Local images - dimensions inferred automatically
|
||||
import heroImage from './hero.png'
|
||||
<Image src={heroImage} alt="Hero" />
|
||||
|
||||
// Remote images - must specify width/height
|
||||
<Image src="https://example.com/image.jpg" alt="Hero" width={800} height={400} />
|
||||
|
||||
// Or use fill for parent-relative sizing
|
||||
<div style={{ position: 'relative', width: '100%', height: 400 }}>
|
||||
<Image src="/hero.png" alt="Hero" fill style={{ objectFit: 'cover' }} />
|
||||
</div>
|
||||
```
|
||||
|
||||
## Remote Images Configuration
|
||||
|
||||
Remote domains must be configured in `next.config.js`:
|
||||
|
||||
```js
|
||||
// next.config.js
|
||||
module.exports = {
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'example.com',
|
||||
pathname: '/images/**',
|
||||
},
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: '*.cdn.com', // Wildcard subdomain
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Responsive Images
|
||||
|
||||
Use `sizes` to tell the browser which size to download:
|
||||
|
||||
```tsx
|
||||
// Full-width hero
|
||||
<Image
|
||||
src="/hero.png"
|
||||
alt="Hero"
|
||||
fill
|
||||
sizes="100vw"
|
||||
/>
|
||||
|
||||
// Responsive grid (3 columns on desktop, 1 on mobile)
|
||||
<Image
|
||||
src="/card.png"
|
||||
alt="Card"
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, 33vw"
|
||||
/>
|
||||
|
||||
// Fixed sidebar image
|
||||
<Image
|
||||
src="/avatar.png"
|
||||
alt="Avatar"
|
||||
width={200}
|
||||
height={200}
|
||||
sizes="200px"
|
||||
/>
|
||||
```
|
||||
|
||||
## Blur Placeholder
|
||||
|
||||
Prevent layout shift with placeholders:
|
||||
|
||||
```tsx
|
||||
// Local images - automatic blur hash
|
||||
import heroImage from './hero.png'
|
||||
<Image src={heroImage} alt="Hero" placeholder="blur" />
|
||||
|
||||
// Remote images - provide blurDataURL
|
||||
<Image
|
||||
src="https://example.com/image.jpg"
|
||||
alt="Hero"
|
||||
width={800}
|
||||
height={400}
|
||||
placeholder="blur"
|
||||
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
|
||||
/>
|
||||
|
||||
// Or use color placeholder
|
||||
<Image
|
||||
src="https://example.com/image.jpg"
|
||||
alt="Hero"
|
||||
width={800}
|
||||
height={400}
|
||||
placeholder="empty"
|
||||
style={{ backgroundColor: '#e0e0e0' }}
|
||||
/>
|
||||
```
|
||||
|
||||
## Priority Loading
|
||||
|
||||
Use `priority` for above-the-fold images (LCP):
|
||||
|
||||
```tsx
|
||||
// Hero image - loads immediately
|
||||
<Image src="/hero.png" alt="Hero" fill priority />
|
||||
|
||||
// Below-fold images - lazy loaded by default (no priority needed)
|
||||
<Image src="/card.png" alt="Card" width={400} height={300} />
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
```tsx
|
||||
// Bad: Missing sizes with fill - downloads largest image
|
||||
<Image src="/hero.png" alt="Hero" fill />
|
||||
|
||||
// Good: Add sizes for proper responsive behavior
|
||||
<Image src="/hero.png" alt="Hero" fill sizes="100vw" />
|
||||
|
||||
// Bad: Using width/height for aspect ratio only
|
||||
<Image src="/hero.png" alt="Hero" width={16} height={9} />
|
||||
|
||||
// Good: Use actual display dimensions or fill with sizes
|
||||
<Image src="/hero.png" alt="Hero" fill sizes="100vw" style={{ objectFit: 'cover' }} />
|
||||
|
||||
// Bad: Remote image without config
|
||||
<Image src="https://untrusted.com/image.jpg" alt="Image" width={400} height={300} />
|
||||
// Error: Invalid src prop, hostname not configured
|
||||
|
||||
// Good: Add hostname to next.config.js remotePatterns
|
||||
```
|
||||
|
||||
## Static Export
|
||||
|
||||
When using `output: 'export'`, use `unoptimized` or custom loader:
|
||||
|
||||
```tsx
|
||||
// Option 1: Disable optimization
|
||||
<Image src="/hero.png" alt="Hero" width={800} height={400} unoptimized />
|
||||
|
||||
// Option 2: Global config
|
||||
// next.config.js
|
||||
module.exports = {
|
||||
output: 'export',
|
||||
images: { unoptimized: true },
|
||||
}
|
||||
|
||||
// Option 3: Custom loader (Cloudinary, Imgix, etc.)
|
||||
const cloudinaryLoader = ({ src, width, quality }) => {
|
||||
return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`
|
||||
}
|
||||
|
||||
<Image loader={cloudinaryLoader} src="sample.jpg" alt="Sample" width={800} height={400} />
|
||||
```
|
||||
Reference in New Issue
Block a user