Analyze performance patterns in XM Cloud Next.js projects
Analyzes XM Cloud Next.js projects for performance issues and provides optimization recommendations.
/plugin marketplace add twofoldtech-dakota/claude-marketplace/plugin install twofoldtech-dakota-xm-cloud-analyzer-plugins-xm-cloud-analyzer@twofoldtech-dakota/claude-marketplaceIdentify performance issues, optimize data fetching, and improve bundle size.
Check for unnecessary SSR on static content:
// Bad: SSR for content that doesn't change per-request
export async function getServerSideProps(context) {
const articles = await fetchArticles(); // Static content
return { props: { articles } };
}
// Good: SSG with revalidation for static content
export async function getStaticProps() {
const articles = await fetchArticles();
return {
props: { articles },
revalidate: 60, // ISR: regenerate every 60 seconds
};
}
Check for unoptimized images:
// Bad: Standard img tag
<img src="/images/hero.jpg" alt="Hero" />
// Good: Next.js Image component
import Image from 'next/image';
<Image
src="/images/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // For above-the-fold images
/>
For Sitecore images:
// Good: JSS Image component
import { Image as JssImage } from '@sitecore-jss/sitecore-jss-nextjs';
<JssImage field={fields.image} />
Check for large imports:
// Bad: Import entire library
import _ from 'lodash';
import moment from 'moment';
// Good: Tree-shakeable imports
import debounce from 'lodash/debounce';
import { format } from 'date-fns';
Check for code splitting on heavy components:
// Bad: Static import of heavy component
import HeavyChart from 'components/HeavyChart';
// Good: Dynamic import with loading state
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('components/HeavyChart'), {
loading: () => <ChartSkeleton />,
ssr: false, // If not needed for SEO
});
Check for missing or improper revalidation:
// Bad: No revalidation (stale forever)
export async function getStaticProps() {
return { props: { data } };
}
// Good: ISR with appropriate interval
export async function getStaticProps() {
return {
props: { data },
revalidate: 300, // 5 minutes
};
}
Check for missing cache headers:
// Bad: No cache headers
export default function handler(req, res) {
res.json(data);
}
// Good: With cache headers
export default function handler(req, res) {
res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate');
res.json(data);
}
Check for font loading strategy:
// Good: Next.js font optimization
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
// Bad: External font link without preconnect
<link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet" />
Check for render-blocking scripts:
// Bad: Blocking script
<script src="https://example.com/widget.js" />
// Good: Next.js Script with strategy
import Script from 'next/script';
<Script
src="https://example.com/widget.js"
strategy="lazyOnload"
/>
| Code | Severity | Issue | Detection |
|---|---|---|---|
| PERF-001 | Critical | getServerSideProps for static content | SSR on pages without dynamic data |
| PERF-002 | Warning | Large bundle imports | Full lodash, moment, etc. |
| PERF-003 | Warning | Images without next/image | <img> tags for local images |
| PERF-004 | Warning | Missing revalidate | getStaticProps without revalidate |
| PERF-005 | Warning | No dynamic imports | Heavy components not code-split |
| PERF-006 | Info | Missing font optimization | External fonts without next/font |
| PERF-007 | Info | No API route caching | API routes without Cache-Control |
| PERF-008 | Info | Blocking third-party scripts | Scripts without strategy |
Grep: getServerSideProps
Grep: getStaticProps
Grep: revalidate
Identify pages using SSR that could be SSG
Grep: <img
Grep: next/image
Grep: @sitecore-jss.*Image
Count unoptimized images
Grep: import .* from 'lodash'
Grep: import .* from 'moment'
Grep: import .* from 'date-fns'
Check for full library imports
Grep: dynamic\(
Glob: **/components/**/*.tsx
Identify heavy components without dynamic import
Grep: getStaticProps
Check for revalidate key in return
## Performance Analysis
### Performance Score: B
### Data Fetching Summary
| Strategy | Pages | Recommendation |
|----------|-------|----------------|
| SSG | 12 | Good |
| SSG + ISR | 5 | Good |
| SSR | 8 | Review (3 could be SSG) |
### Critical Issues
#### [PERF-001] SSR for Static Content
**Location**: `src/pages/about.tsx`
**Issue**: Using getServerSideProps for page with static content
**Code**:
```typescript
export async function getServerSideProps() {
const content = await fetchAboutContent();
return { props: { content } };
}
Impact: Every request fetches data, no caching benefit Fix:
export async function getStaticProps() {
const content = await fetchAboutContent();
return {
props: { content },
revalidate: 3600, // Revalidate hourly
};
}
Location: src/lib/helpers/dates.ts:1
Issue: Importing entire moment.js library
Code:
import moment from 'moment';
Impact: Adds ~300KB to bundle Fix: Use date-fns with tree shaking:
import { format, parseISO } from 'date-fns';
Locations:
src/components/Hero/Hero.tsx:15src/components/Card/Card.tsx:23
Issue: Using <img> instead of <Image> component
Fix: Use Next.js Image or JSS Image component| Import | Size | Alternative |
|---|---|---|
| moment | ~300KB | date-fns (~20KB) |
| lodash | ~70KB | lodash-es (tree-shake) |
Designs feature architectures by analyzing existing codebase patterns and conventions, then providing comprehensive implementation blueprints with specific files to create/modify, component designs, data flows, and build sequences