/ astro-integrations / How to Integrate ImageKit with Astro: Complete Guide
astro-integrations 5 min read

How to Integrate ImageKit with Astro: Complete Guide

Step-by-step guide to integrating ImageKit with your Astro website.

ImageKit is a real-time image and video optimization service with a global CDN. You upload your original high-resolution images once, and ImageKit serves optimized versions on the fly. Need a 400px wide WebP thumbnail? Just add URL parameters. Need the same image as a 1200px AVIF? Change the parameters. No pre-processing, no build step, no storing multiple versions.

For Astro sites, this means you can stop worrying about image optimization during the build process. Upload originals to ImageKit, reference them with transformation parameters in your templates, and every visitor gets the right format and size for their device.

Prerequisites

  • Node.js 18+
  • An Astro project (npm create astro@latest)
  • An ImageKit account (free tier includes 20GB bandwidth/month and 20GB storage)

Installation

Install the ImageKit SDK:

npm install imagekitio-core

For upload functionality from the server side:

npm install imagekit

Configuration

Get your credentials from the ImageKit dashboard under Developer Options:

  • URL Endpoint (your unique ImageKit URL)
  • Public Key (for client-side uploads)
  • Private Key (for server-side operations)

Add them to your .env:

PUBLIC_IMAGEKIT_URL=https://ik.imagekit.io/your_id
IMAGEKIT_PUBLIC_KEY=public_xxxx
IMAGEKIT_PRIVATE_KEY=private_xxxx

Create the ImageKit client:

// src/lib/imagekit.ts
import ImageKit from "imagekit";

export const imagekit = new ImageKit({
  publicKey: import.meta.env.IMAGEKIT_PUBLIC_KEY,
  privateKey: import.meta.env.IMAGEKIT_PRIVATE_KEY,
  urlEndpoint: import.meta.env.PUBLIC_IMAGEKIT_URL,
});

export function getImageUrl(
  path: string,
  transforms: { width?: number; height?: number; quality?: number; format?: string } = {}
): string {
  const params: string[] = [];

  if (transforms.width) params.push(`w-${transforms.width}`);
  if (transforms.height) params.push(`h-${transforms.height}`);
  if (transforms.quality) params.push(`q-${transforms.quality}`);
  if (transforms.format) params.push(`f-${transforms.format}`);

  const transformString = params.length > 0 ? `/tr:${params.join(",")}` : "";
  return `${import.meta.env.PUBLIC_IMAGEKIT_URL}${transformString}/${path}`;
}

Basic Usage

Use ImageKit URLs directly in your Astro components with URL-based transformations:

---
// src/components/OptimizedImage.astro
export interface Props {
  src: string;
  alt: string;
  width?: number;
  height?: number;
  quality?: number;
}

const { src, alt, width = 800, height, quality = 80 } = Astro.props;
const baseUrl = import.meta.env.PUBLIC_IMAGEKIT_URL;

// Build transformation string
const transforms = [`w-${width}`, `q-${quality}`];
if (height) transforms.push(`h-${height}`);

const imageUrl = `${baseUrl}/tr:${transforms.join(",")}/${src}`;
const webpUrl = `${baseUrl}/tr:${transforms.join(",")},f-webp/${src}`;
const avifUrl = `${baseUrl}/tr:${transforms.join(",")},f-avif/${src}`;
---

<picture>
  <source srcset={avifUrl} type="image/avif" />
  <source srcset={webpUrl} type="image/webp" />
  <img src={imageUrl} alt={alt} width={width} height={height} loading="lazy" />
</picture>

Use it in your pages:

---
import OptimizedImage from "../components/OptimizedImage.astro";
---

<OptimizedImage src="blog/hero-image.jpg" alt="Article hero" width={1200} height={630} />

Responsive Images

Generate responsive image sets with a single source image:

---
export interface Props {
  src: string;
  alt: string;
  sizes?: string;
}

const { src, alt, sizes = "(max-width: 768px) 100vw, 800px" } = Astro.props;
const baseUrl = import.meta.env.PUBLIC_IMAGEKIT_URL;

const widths = [400, 600, 800, 1200, 1600];
const srcset = widths
  .map((w) => `${baseUrl}/tr:w-${w},f-auto,q-80/${src} ${w}w`)
  .join(", ");
---

<img
  src={`${baseUrl}/tr:w-800,f-auto,q-80/${src}`}
  srcset={srcset}
  sizes={sizes}
  alt={alt}
  loading="lazy"
  decoding="async"
/>

The f-auto parameter tells ImageKit to serve the best format for each browser automatically. Chrome gets AVIF, Safari gets WebP, older browsers get JPEG.

Server-Side Uploads

Create an API route for uploading images to ImageKit:

// src/pages/api/upload-image.ts
import type { APIRoute } from "astro";
import { imagekit } from "../../lib/imagekit";

export const POST: APIRoute = async ({ request }) => {
  try {
    const formData = await request.formData();
    const file = formData.get("file") as File;
    const folder = (formData.get("folder") as string) || "/blog";

    if (!file) {
      return new Response(JSON.stringify({ error: "No file provided" }), {
        status: 400,
      });
    }

    const buffer = Buffer.from(await file.arrayBuffer());
    const base64 = buffer.toString("base64");

    const result = await imagekit.upload({
      file: base64,
      fileName: file.name,
      folder: folder,
    });

    return new Response(
      JSON.stringify({
        url: result.url,
        filePath: result.filePath,
        fileId: result.fileId,
      }),
      { status: 200, headers: { "Content-Type": "application/json" } }
    );
  } catch (error) {
    console.error("Upload error:", error);
    return new Response(JSON.stringify({ error: "Upload failed" }), {
      status: 500,
    });
  }
};

Using ImageKit in Blog Posts

Reference images in your MDX frontmatter with ImageKit paths:

---
heroImage: "blog/my-article/hero.jpg"
---

Then in your blog layout, transform on the fly:

---
const { heroImage } = post.data;
const baseUrl = import.meta.env.PUBLIC_IMAGEKIT_URL;
const heroUrl = heroImage
  ? `${baseUrl}/tr:w-1200,h-630,f-auto,q-85/${heroImage}`
  : null;
---

{heroUrl && <img src={heroUrl} alt={post.data.heroImageAlt} width={1200} height={630} />}

Production Tips

  1. Use named transforms. In the ImageKit dashboard, create named transforms for common sizes (thumbnail, hero, og-image). Use them with n-transform_name in URLs for cleaner markup.

  2. Enable automatic format selection. The f-auto parameter serves the best format per browser. This alone can cut image sizes by 30-50% compared to serving JPEG everywhere.

  3. Set up a custom domain. Map images.yourdomain.com to your ImageKit endpoint. This improves perceived trust and SEO since images come from your domain.

  4. Purge cache when needed. ImageKit caches transformations on their CDN. If you replace an image with the same filename, purge the cache from the dashboard or API to see changes immediately.

  5. Use overlays for social images. ImageKit can add text and image overlays via URL parameters. Generate Open Graph images dynamically without a build step: add your blog title as a text overlay on a template background.

Alternatives to Consider

  • Cloudinary if you need more advanced video processing and AI-based transformations, and the free tier is sufficient (25 monthly transformations).
  • Cloudflare Images if you are already on Cloudflare and want simpler flat-rate pricing per image served.
  • Astro's built-in image optimization if your site is fully static and you do not need a CDN or dynamic transformations.

Wrapping Up

ImageKit removes image optimization from your build process and moves it to the edge. For Astro sites with lots of visual content, this means faster builds, smaller page sizes, and automatic format negotiation for every visitor. The URL-based transformation API is simple to use in Astro templates, and the free tier is generous enough for most blogs and portfolios. Upload your originals, build your URLs with the right parameters, and let ImageKit handle the rest.