436
Documentation

Image Optimization

Automatic image optimization with WebP/AVIF support, lazy loading, responsive images, and remote image support.

Introduction

Loly Framework includes a complete image optimization system similar to Next.js Image, with support for local and remote images, automatic format conversion, lazy loading, and more.

Important: Always use the Image component from @lolyjs/core/components, never from Next.js. Loly Framework has its own optimized image system that provides all the features you need.

Features

  • Automatic optimization - Resizing, compression, and format conversion
  • Modern formats - Support for WebP and AVIF
  • Remote images - Support for images from external domains (with whitelist)
  • Lazy loading - Deferred loading by default
  • Responsive images - Automatic srcset generation
  • Smart cache - Disk cache for better performance
  • CLS prevention - Aspect ratio containers to avoid layout shift

Basic Usage

Local Image

app/page.tsx
import { Image } from "@lolyjs/core/components";

export default function MyPage() {
  return (
    <Image
      src="/assets/hero.jpg"
      alt="Hero image"
      width={800}
      height={600}
    />
  );
}

Remote Image

Remote images require configuration in loly.config.ts. See the Remote Images section below.

import { Image } from "@lolyjs/core/components";

<Image
  src="https://example.com/image.jpg"
  alt="Remote image"
  width={1200}
  height={800}
/>

Configuration

Configure image optimization settings in loly.config.ts:

loly.config.ts
import type { FrameworkConfig } from "@lolyjs/core";

export default {
  images: {
    // Device sizes for srcset generation
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    
    // Image sizes for srcset
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    
    // Supported formats
    formats: ["image/webp", "image/avif"],
    
    // Default quality (1-100)
    quality: 75,
    
    // Minimum cache TTL in seconds
    minimumCacheTTL: 60,
    
    // Allow SVG (security risk if enabled)
    dangerouslyAllowSVG: false,
    
    // Content Security Policy for SVG
    contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
    
    // Maximum sizes
    maxWidth: 3840,
    maxHeight: 3840,
  },
} satisfies Partial<FrameworkConfig>;

Remote Images

To use remote images, you must configure allowed domains in loly.config.ts:

loly.config.ts
import type { FrameworkConfig } from "@lolyjs/core";

export default {
  images: {
    // Option 1: Use remotePatterns (recommended)
    remotePatterns: [
      {
        protocol: "https",
        hostname: "images.unsplash.com",
      },
      {
        protocol: "https",
        hostname: "**.unsplash.com", // Wildcard for subdomains
        pathname: "/photos/**",      // Optional: restrict path
      },
    ],
    
    // Option 2: Use domains (legacy, Next.js style)
    // domains: ['images.unsplash.com', 'cdn.example.com'],
  },
} satisfies Partial<FrameworkConfig>;

Security: All remote domains must be whitelisted. If you try to use an image from a non-allowed domain, you will get a 403 error.

Image Component Props

Basic Props

interface ImageProps {
  src: string;              // Image path (local or remote)
  alt: string;              // Alternative text (required)
  width?: number;           // Width in pixels
  height?: number;          // Height in pixels
  className?: string;       // CSS classes
}

Optimization Props

{
  quality?: number;         // Quality (1-100, default: 75)
  format?: 'webp' | 'avif' | 'jpeg' | 'png' | 'auto'; // Forced format
  priority?: boolean;       // Load immediately (disables lazy loading)
  fill?: boolean;           // Fill mode (fills parent container)
  sizes?: string;           // Sizes attribute for responsive images
  placeholder?: 'blur' | 'empty'; // Placeholder type
  blurDataURL?: string;     // Blur placeholder URL
}

Examples

Image with Custom Quality

<Image
  src="/assets/photo.jpg"
  alt="Photo"
  width={800}
  height={600}
  quality={90}  // High quality
/>

Responsive Image

The component automatically generates a srcset with different sizes based on configured deviceSizes.

<Image
  src="/assets/hero.jpg"
  alt="Hero"
  width={1920}
  height={1080}
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 1920px"
/>

Priority Loading

<Image
  src="/assets/logo.png"
  alt="Logo"
  width={200}
  height={100}
  priority  // Loads immediately (above-the-fold)
/>

Fill Mode

<div className="relative h-64 w-full">
  <Image
    src="/assets/background.jpg"
    alt="Background"
    fill  // Fills parent container
    className="object-cover"
  />
</div>

Remote Image with Format Conversion

<Image
  src="https://images.unsplash.com/photo-123"
  alt="Unsplash photo"
  width={1200}
  height={800}
  format="webp"  // Automatically converts to WebP
  quality={85}
/>

Lazy Loading (Default)

Lazy loading is enabled by default. Images load when scrolled into view.

// Lazy loading is enabled by default
<Image
  src="/assets/image.jpg"
  alt="Image"
  width={600}
  height={400}
  // No priority = automatic lazy loading
/>

Complete Example

import { Image } from "@lolyjs/core/components";

export default function Gallery() {
  const images = [
    { id: 1, url: "/assets/img1.jpg", alt: "Image 1" },
    { id: 2, url: "/assets/img2.jpg", alt: "Image 2" },
    { id: 3, url: "/assets/img3.jpg", alt: "Image 3" },
  ];

  return (
    <div className="grid grid-cols-3 gap-4">
      {images.map((img) => (
        <Image
          key={img.id}
          src={img.url}
          alt={img.alt}
          width={400}
          height={300}
          quality={80}
          sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 400px"
          className="rounded-lg"
        />
      ))}
    </div>
  );
}

Optimization Endpoint

The framework exposes an internal endpoint /_loly/image that processes images:

/_loly/image?src=/path/to/image.jpg&w=800&h=600&q=75&format=webp

Endpoint Parameters

  • src - Image path (required)
  • w - Width in pixels (optional)
  • h - Height in pixels (optional)
  • q - Quality (1-100, optional)
  • format - Output format: webp, avif, jpeg, png, auto (optional)
  • fit - Fit mode: contain, cover, fill, inside, outside (optional)

Cache

Optimized images are automatically cached in .loly/cache/images/. The cache is based on:

  • Image URL
  • Dimensions (width/height)
  • Quality
  • Format

This means the same image with the same parameters is only processed once.

Security

Remote Domain Validation

For security, all remote domains must be whitelisted. If you try to use an image from a non-allowed domain, you will get a 403 error.

// ✅ Allowed (configured in loly.config.ts)
<Image src="https://images.unsplash.com/photo.jpg" ... />

// ❌ Blocked (domain not allowed)
<Image src="https://malicious-site.com/image.jpg" ... />

SVG

By default, SVG images are disabled for security reasons. To enable them:

loly.config.ts
images: {
  dangerouslyAllowSVG: true,
  contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
}

Best Practices

1. Always Provide width and height

// ✅ Correct - prevents CLS
<Image src="/photo.jpg" alt="Photo" width={800} height={600} />

// ❌ Incorrect - can cause layout shift
<Image src="/photo.jpg" alt="Photo" />

2. Use priority for Above-the-Fold

// ✅ Hero image loads immediately
<Image src="/hero.jpg" alt="Hero" width={1920} height={1080} priority />

// ✅ Images further down use lazy loading
<Image src="/gallery-1.jpg" alt="Gallery" width={400} height={300} />

3. Configure sizes for Responsive Images

<Image
  src="/hero.jpg"
  alt="Hero"
  width={1920}
  height={1080}
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 1920px"
/>

4. Use Modern Formats

The framework automatically converts to WebP or AVIF if enabled in configuration. This can reduce file size by 50-70%.

5. Adjust Quality Based on Use Case

// High quality for important photos
<Image src="/hero.jpg" quality={90} ... />

// Medium quality for thumbnails
<Image src="/thumb.jpg" quality={60} ... />

Troubleshooting

Error: "Remote image domain not allowed"

Problem: You're trying to use an image from a non-configured domain.

Solution: Add the domain to remotePatterns or domains in loly.config.ts:

loly.config.ts
images: {
  remotePatterns: [
    { protocol: 'https', hostname: 'your-domain.com' }
  ]
}

Error: "Image not found"

Problem: The local image doesn't exist at the specified path.

Solution: Verify that the image is in the public/ directory and the path is correct:

// ✅ Correct - image in public/assets/logo.png
<Image src="/assets/logo.png" ... />

// ❌ Incorrect - wrong path
<Image src="/logo.png" ... />

Images are not optimized

Problem: Images are served directly without optimization.

Solution: Make sure you're using the Image component from @lolyjs/core/components, not a regular HTML <img> tag.

SVG doesn't work

Problem: SVG images are not processed.

Solution: Enable SVG in configuration (with caution):

loly.config.ts
images: {
  dangerouslyAllowSVG: true,
}

Performance

The image optimization system can significantly improve performance:

  • Size reduction: 50-70% smaller with WebP/AVIF
  • Load time: 30-50% faster
  • Improved LCP: 20-40% improvement
  • Reduced CLS: 30-50% less layout shift