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
Use named transforms. In the ImageKit dashboard, create named transforms for common sizes (thumbnail, hero, og-image). Use them with
n-transform_namein URLs for cleaner markup.Enable automatic format selection. The
f-autoparameter serves the best format per browser. This alone can cut image sizes by 30-50% compared to serving JPEG everywhere.Set up a custom domain. Map
images.yourdomain.comto your ImageKit endpoint. This improves perceived trust and SEO since images come from your domain.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.
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.
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.