Largest Contentful Paint (LCP) measures how quickly the main content of your Directus-powered site loads. Since Directus is headless, LCP optimization focuses on both backend API performance and frontend rendering efficiency.
Target: LCP under 2.5 seconds Good: Under 2.5s | Needs Improvement: 2.5-4.0s | Poor: Over 4.0s
For general LCP concepts, see the global LCP guide.
Directus-Specific LCP Issues
1. Slow API Response Time (TTFB)
The most critical factor is how quickly your Directus API responds.
Diagnosis:
- Open Chrome DevTools → Network tab
- Look for requests to your Directus API
- Check "Waiting (TTFB)" time
- Target: TTFB under 200ms
Solutions:
A. Enable Directus Caching
# .env in Directus
CACHE_ENABLED=true
CACHE_STORE=redis
CACHE_REDIS=redis://localhost:6379
CACHE_TTL=30m
B. Optimize Directus Queries
// Bad - fetches everything
const articles = await directus.request(
readItems('articles', {
fields: ['*', '*.*.*'],
})
);
// Good - only fetch needed fields
const articles = await directus.request(
readItems('articles', {
fields: ['id', 'title', 'slug', 'featured_image.id'],
limit: 10,
})
);
C. Use GraphQL for Precise Data Fetching
// Fetch exactly what you need in one request
const { data } = await fetch(`${DIRECTUS_URL}/graphql`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `
query GetArticle($slug: String!) {
articles(filter: { slug: { _eq: $slug } }) {
id
title
content
featured_image {
id
filename_disk
}
}
}
`,
variables: { slug },
}),
});
2. Unoptimized Images from Directus
Solutions:
A. Use Directus Image Transformations
// Next.js Image component with Directus
import Image from 'next/image';
const directusImageLoader = ({ src, width, quality }) => {
return `${process.env.NEXT_PUBLIC_DIRECTUS_URL}/assets/${src}?width=${width}&quality=${quality || 75}&format=webp`;
};
<Image
loader={directusImageLoader}
src={article.featured_image.id}
alt={article.title}
width={1200}
height={600}
priority={true} // For LCP images
/>
B. Preload LCP Image
// app/articles/[slug]/page.tsx
export default function ArticlePage({ article }) {
return (
<>
<link
rel="preload"
as="image"
href={`${process.env.NEXT_PUBLIC_DIRECTUS_URL}/assets/${article.featured_image.id}?width=1200&format=webp`}
/>
<article>{/* Content */}</article>
</>
);
}
3. Framework Rendering Strategy
export async function generateStaticParams() {
const articles = await directus.request(readItems('articles', { fields: ['slug'] }));
return articles.map(article => ({ slug: article.slug }));
}
export default async function ArticlePage({ params }: { params: { slug: string } }) {
const article = await directus.request(
readItems('articles', {
filter: { slug: { _eq: params.slug } },
})
);
return <ArticleContent article={article[0]} />;
}
Quick Wins Checklist
- Enable Directus cache (Redis)
- Optimize Directus API queries (limit fields)
- Use Directus image transformations
- Implement SSG/ISR instead of SSR
- Preload LCP images
- Use GraphQL for precise data fetching
- Enable CDN for Directus assets
- Implement code splitting
- Use font-display: swap
Next Steps
For general LCP optimization strategies, see LCP Optimization Guide.