Fix LCP Issues on Cosmicjs (Loading Speed) | OpsBlu Docs

Fix LCP Issues on Cosmicjs (Loading Speed)

Improve Cosmic JS LCP by using Imgix image transforms, caching API responses at the edge, and statically generating content pages.

General Guide: See Global LCP Guide for universal concepts and fixes.

What is LCP?

Largest Contentful Paint measures when the largest content element becomes visible. Google recommends LCP under 2.5 seconds. Cosmic (formerly Cosmic JS) is a headless CMS, so LCP depends on your frontend framework's rendering strategy, Cosmic API response times, and how you handle media from Cosmic's Imgix-powered CDN.

Cosmic-Specific LCP Causes

  • Client-side API fetching -- if your frontend fetches Cosmic objects in the browser, LCP waits for JS + API call + render
  • Unoptimized Imgix URLs -- Cosmic serves media through Imgix CDN but most implementations use default URLs without transform parameters
  • Large API payloads -- fetching full Object metadata and content when only title and thumbnail are needed
  • Frontend hydration delay -- React/Next.js/Gatsby SPA hydration adds 500-2000ms before LCP content appears
  • No caching layer -- API responses fetched fresh on every request without edge caching

Fixes

1. Use SSG/SSR with Cosmic SDK

Fetch content at build time or server-side to eliminate the client-side API waterfall:

// Next.js with Cosmic SDK -- getStaticProps for SSG
import { createBucketClient } from '@cosmicjs/sdk';

const cosmic = createBucketClient({
  bucketSlug: process.env.COSMIC_BUCKET_SLUG,
  readKey: process.env.COSMIC_READ_KEY,
});

export async function getStaticProps() {
  const { object } = await cosmic.objects.findOne({
    type: 'pages',
    slug: 'home',
  }).props('title,slug,metadata.hero_image,metadata.hero_title');

  return {
    props: { page: object },
    revalidate: 60, // ISR: revalidate every 60 seconds
  };
}

2. Use Imgix Transforms on Cosmic Media

Cosmic serves all media through Imgix CDN. Append transform parameters to optimize delivery:

// Cosmic Imgix URL optimization helper
function optimizeCosmicImage(imgixUrl, { width = 800, quality = 80, format = 'auto' } = {}) {
  if (!imgixUrl) return '';
  const separator = imgixUrl.includes('?') ? '&' : '?';
  return `${imgixUrl}${separator}w=${width}&q=${quality}&auto=format,compress&fit=crop`;
}

// React component with responsive images
function HeroImage({ imageUrl, alt }) {
  return (
    <img
      src={optimizeCosmicImage(imageUrl, { width: 1920 })}
      srcSet={`
        ${optimizeCosmicImage(imageUrl, { width: 640 })} 640w,
        ${optimizeCosmicImage(imageUrl, { width: 1024 })} 1024w,
        ${optimizeCosmicImage(imageUrl, { width: 1920 })} 1920w
      `}
      sizes="100vw"
      width="1920"
      height="600"
      alt={alt || ''}
      loading="eager"
      fetchPriority="high"
    />
  );
}

Key Imgix parameters for Cosmic images:

  • w=800 -- width constraint
  • q=80 -- quality (80 is good balance)
  • auto=format,compress -- auto-selects WebP/AVIF and applies compression
  • fit=crop -- crops to exact dimensions
  • blur=200 -- useful for low-quality image placeholder (LQIP) technique

3. Reduce API Payload with Props Selection

Use Cosmic SDK's .props() method to request only needed fields:

// BAD: Fetch full objects with all metadata
const { objects } = await cosmic.objects.find({ type: 'posts' });

// GOOD: Fetch only what you need
const { objects } = await cosmic.objects
  .find({ type: 'posts' })
  .props('title,slug,metadata.thumbnail,metadata.excerpt,created_at')
  .limit(10);

4. Preload LCP Image

// Next.js -- preload hero image in document head
import Head from 'next/head';

function PageHead({ heroImage }) {
  return (
    <Head>
      <link rel="preconnect" href="https://imgix.cosmicjs.com" />
      {heroImage && (
        <link
          rel="preload"
          as="image"
          href={optimizeCosmicImage(heroImage, { width: 1920 })}
          type="image/webp"
        />
      )}
    </Head>
  );
}

5. Cache API Responses at the Edge

For SSR deployments, cache Cosmic API responses to reduce TTFB:

// Edge-cached API wrapper
const CACHE_TTL = 300; // 5 minutes

async function getCachedObject(type, slug) {
  const cacheKey = `cosmic:${type}:${slug}`;

  // Check edge cache (e.g., Vercel Edge Config, Cloudflare KV)
  const cached = await edgeCache.get(cacheKey);
  if (cached) return JSON.parse(cached);

  const { object } = await cosmic.objects.findOne({ type, slug });
  await edgeCache.set(cacheKey, JSON.stringify(object), { ttl: CACHE_TTL });
  return object;
}

Measuring LCP on Cosmic

  1. Cosmic Dashboard -- check API usage under Settings > API to monitor response times
  2. Chrome DevTools Network tab -- filter for api.cosmicjs.com requests to see payload sizes and response times
  3. Compare SSG vs CSR -- test the same page built statically vs. fetched client-side to quantify the difference
  4. Test key page types: landing pages (hero images), blog listings (many thumbnails), and individual posts (featured images)

Analytics Script Impact

Cosmic is headless, so analytics loading depends on your frontend:

  • Next.js -- use next/script with strategy="afterInteractive" for GTM/GA
  • Gatsby -- use gatsby-plugin-google-gtag which handles async loading
  • Astro -- place analytics in <script> tags with is:inline or defer