directus-image-presets
v0.1.0
Published
Resolve Directus file IDs to optimized URLs across S3/CloudFront presets, with fallback to native Directus assets. Skip paying for Next.js Image optimization.
Maintainers
Readme
directus-image-presets
Resolve Directus file references (UUIDs, file objects, arrays, legacy formats) into optimized image URLs — served from S3/CloudFront when available, with automatic fallback to native Directus assets.
npm install directus-image-presetsWhy
Directus ships server-side image presets, but the moment you put a CDN in front of S3-baked derivatives, every consumer has to:
- detect whether a string is a UUID, a Directus URL, an S3 URL, or already-optimized
- pick the right preset folder
- fall back to Directus when the derivative doesn't exist yet
- handle nested
directus_files_idshapes - ditto for arrays of any of the above
This package does all of that, behind one resolver you configure once. Skip Next.js Image optimization fees by serving pre-baked WebP from your own CDN.
Quick start
import { createDirectusImageResolver } from "directus-image-presets";
export const images = createDirectusImageResolver({
directusUrl: "https://cms.example.com",
cdnUrl: "https://d2qyp4pqjcr206.cloudfront.net",
s3Bucket: "my-app-images",
presetFolders: {
avatar: "avatar", // 100x100
card: "card", // 400x400
hero: "hero", // 1200w
og: "og", // 1200x630
webp70: "webp70", // general purpose
},
});
// Anywhere:
images.url(post.cover_image, "hero");
// → "https://d2qyp4pqjcr206.cloudfront.net/optimized/hero/<uuid>.webp"
images.url("9d657d01-9a28-4f11-a7c5-1cfb28384187", "card");
images.url({ directus_files_id: { id: "..." } }, "avatar");
images.url([null, file, "fallback-id"], "card"); // first non-null winsFallback to Directus
Sometimes the CDN derivative hasn't been generated yet. Use withFallback and switch on the client:
const { primary, fallback } = images.withFallback(post.cover, "hero");
<img src={primary} onError={(e) => { e.currentTarget.src = fallback; }} />Or use the bundled React component:
import { DirectusImage } from "directus-image-presets/react";
<DirectusImage resolver={images} image={post.cover} preset="hero" alt="" />Why the S3 layout?
Run a Lambda that subscribes to Directus file uploads, optimizes them with sharp, and writes:
s3://my-app-images/optimized/<preset>/<uuid>.<ext>Point CloudFront at the bucket — it now serves your derivatives globally for ~$0.085/GB with zero compute on read.
API
createDirectusImageResolver(options): DirectusImageResolver
isDirectusFileId(value: string): boolean
resolver.url(image, preset?, fallback?): string
resolver.urlList(images, preset?, max?): string[]
resolver.withFallback(image, preset?): { primary, fallback, fileId }
resolver.isOptimizedUrl(url): boolean
resolver.directUrl(image, preset?, fallback?): string // bypass CDNOptions
| Option | Default | Description |
| --- | --- | --- |
| directusUrl | — | Required. Public Directus URL |
| cdnUrl | — | CloudFront/CDN URL. Omit → always serve from Directus |
| s3Bucket | — | S3 bucket name; lets the resolver recognize raw S3 URLs in inputs |
| s3Region | eu-central-1 | S3 region |
| optimizedPrefix | optimized | Key prefix for derivatives |
| derivativeExtension | webp | Output extension |
| presetFolders | {} | Preset alias → folder name |
| defaultFolder | preset name | Folder used when a preset isn't in presetFolders |
| useCdn | true (if cdnUrl provided) | Set false for admin previews |
| fallback | /images/placeholder.jpg | Returned when input is unrecognizable |
Input types the resolver accepts
- UUID string (
"9d657d01-...") - Directus URL (
"https://cms.example.com/assets/<uuid>") - S3 URL (
"https://my-bucket.s3.eu-central-1.amazonaws.com/...") { directus_files_id: "uuid" }{ directus_files_id: { id: "uuid", ... } }{ id: "uuid" }{ filename_disk: "<uuid>.png" }- Arrays of any of the above (first non-null wins)
null/undefined→ fallback
License
MIT
