AgentStack
SKILL verified MIT Self-run

Seo Performance

skill-mvstepanek-nextjs-ecommerce-seo-skills-seo-performance · by mvstepanek

Core Web Vitals and performance SEO rules for Next.js. Use when optimizing page load speed, addressing LCP/CLS/INP issues, working with third-party scripts like Google Tag Manager, fonts, or bundle size.

No reviews yet
0 installs
5 views
0.0% view→install

Install

$ agentstack add skill-mvstepanek-nextjs-ecommerce-seo-skills-seo-performance

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Seo Performance? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Performance & Core Web Vitals SEO Guidelines

Core Web Vitals are a Google ranking factor. Follow these rules to maintain good performance on a Next.js e-commerce site.

Targets

| Metric | Target | What it measures | |--------|--------|-----------------| | LCP (Largest Contentful Paint) | ; }

// GOOD — Only the interactive part is a client component // components/AddToCartButton.tsx "use client"; export function AddToCartButton({ productId }: Props) { const [quantity, setQuantity] = useState(1); // ... }


**Why it matters**: Client-side rendering adds to the JavaScript bundle. On category pages with 50+ products, making each product card a client component can add hundreds of KB of JS, destroying LCP.

## LCP Optimization

The LCP element on product pages is typically the main product image. On category pages, it's the first visible product image or the hero banner.

1. **Preload the LCP image** — use `priority` prop on the `` component
2. **Avoid client-side data fetching for above-fold content** — fetch product data server-side
3. **Don't lazy-load the LCP image** — `priority` disables lazy loading automatically
4. **Minimize server response time** — use ISR (Incremental Static Regeneration) for product pages

```typescript
// next.config.js — ISR for product pages
// In page.tsx:
export const revalidate = 300; // 5 minutes — fresh enough for buyers

CLS Prevention

  1. Always size images — use width/height or fill with a sized container (see seo-images skill)
  2. Reserve space for dynamic content — prices, stock badges, add-to-cart buttons
  3. Font loading — use next/font with display: swap and size-adjust to prevent layout shift
  4. Avoid injecting content above existing content — banners, promo bars, cookie consents should not push content down
// GOOD — font with swap and size-adjust
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], display: 'swap' });

// BAD — loading fonts via  causes FOUT and CLS

INP Optimization

  1. Minimize client-side JavaScript — less JS = faster interactions
  2. Use useTransition for non-urgent UI updates (filter changes, sort changes on category pages)
  3. Avoid blocking the main thread — don't run expensive computations in event handlers
  4. Debounce search input — don't fire a request on every keystroke
"use client";
import { useTransition } from 'react';

function CategoryFilters({ onFilterChange }: Props) {
  const [isPending, startTransition] = useTransition();

  function handleFilterChange(filter: string) {
    startTransition(() => {
      onFilterChange(filter); // Non-urgent, won't block interaction
    });
  }
}

Google Tag Manager (GTM)

GTM is a significant performance cost if loaded incorrectly.

// GOOD — load GTM after page is interactive
import Script from 'next/script';

// BAD — blocks page rendering

Rules:

  • Always use strategy="afterInteractive" for GTM
  • Never use beforeInteractive for analytics/tracking scripts
  • Store the GTM container ID in an environment variable (NEXT_PUBLIC_GTM_ID), never hardcode it
  • Consider using strategy="lazyOnload" for non-critical third-party scripts (chat widgets, surveys)

Font Loading

  • Always use next/font — it self-hosts fonts, eliminating external requests
  • Set display: 'swap' — shows fallback text immediately, swaps to custom font when loaded
  • Limit font weights — only load weights you actually use (e.g. 400 and 700, not all 9 weights)
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin', 'latin-ext'], // Include latin-ext for European languages
  display: 'swap',
  weight: ['400', '500', '700'], // Only weights actually used
});

Bundle Size

  1. Dynamic imports for below-fold components — product image gallery, reviews section, related products
  2. Avoid importing entire librariesimport { format } from 'date-fns' not import dayjs from 'dayjs' if only formatting
  3. Analyze bundle — use @next/bundle-analyzer to find oversized dependencies
// GOOD — dynamic import for below-fold content
import dynamic from 'next/dynamic';

const ProductReviews = dynamic(() => import('./ProductReviews'), {
  loading: () => ,
});

const RelatedProducts = dynamic(() => import('./RelatedProducts'));

Streaming & Suspense

Use React Suspense to stream content progressively — the page shell renders immediately while slower data loads:

import { Suspense } from 'react';

export default function ProductPage({ params }: Props) {
  return (
    <>
        {/* Renders immediately */}
      }>
          {/* Streams when ready */}
      
      }>
          {/* Streams when ready */}
      
    
  );
}

Common Technical Anti-Patterns

  1. "use client" on data-display components — product cards, breadcrumbs, specs tables don't need client-side JS
  2. GTM with beforeInteractive — blocks rendering, destroys LCP
  3. Loading fonts via `` — use next/font instead
  4. Loading all font weights — only load what you use
  5. Synchronous data fetching in client components — fetch on the server, pass data as props
  6. Not using Suspense — causes the entire page to wait for the slowest data source
  7. Large dependencies for simple tasks — don't import moment.js for a date format
  8. Hardcoded GTM container IDs — use environment variables

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.