Part 2: Advanced Debugging Strategies for Sitecore XM Cloud Development

Welcome back to our comprehensive debugging series for Sitecore XM Cloud Next.js applications! In Part 1, we covered the essential debugging techniques that form the foundation of your development workflow: VS Code integrated debugging, Sitecore JSS debug logging, custom debug logging, and programmatic debugging.

Part 2 dives into advanced debugging strategies that will make you a debugging master:

  • Console-Based Debugging with server vs client awareness
  • Browser DevTools for frontend debugging excellence
  • XM Cloud-Specific Debugging Techniques for complex scenarios

These advanced techniques will help you tackle the most challenging debugging scenarios in your XM Cloud projects.

5. Console-Based Debugging: Server vs Client Awareness

Understanding where your logs appear is crucial in Next.js development. This fundamental debugging approach helps you track data flow and identify issues quickly.

Step-by-Step Setup and Usage

Step 1: Understanding the Execution Context

In Next.js, code can run in different contexts:

  • Server-side – During SSR, SSG, or API routes (logs appear in terminal)
  • Client-side – In the browser after hydration (logs appear in browser DevTools)
  • Universal – Code that runs in both contexts

Step 2: Server-Side Console Debugging

// pages/products/[slug].js - getServerSideProps runs on server
export async function getServerSideProps(context) {
  console.log('[SERVER] getServerSideProps called');
  console.log('[SERVER] Params:', context.params);
  console.log('[SERVER] Query:', context.query);
  console.log('[SERVER] Headers:', context.req.headers);
  
  const { slug } = context.params;
  
  try {
    const product = await fetchProductFromXMCloud(slug);
    console.log('[SERVER] Product fetched:', product.name);
    
    return { props: { product } };
  } catch (error) {
    console.error('[SERVER] Error fetching product:', error.message);
    return { notFound: true };
  }
}

// API routes always run on server
// pages/api/products/[id].js
export default async function handler(req, res) {
  console.log('[API] Request method:', req.method);
  console.log('[API] Request URL:', req.url);
  console.log('[API] Request body:', req.body);
  
  // These logs appear in your terminal/VS Code terminal
  const { id } = req.query;
  console.log('[API] Processing product ID:', id);
  
  res.json({ message: 'Product processed' });
}

Step 3: Client-Side Console Debugging

// components/ProductDetail.jsx - Runs in browser
import { useEffect, useState } from 'react';

export default function ProductDetail({ product }) {
  const [isLoading, setIsLoading] = useState(false);
  
  // This runs in the browser - logs appear in DevTools
  console.log('[CLIENT] Component rendering with product:', product);
  
  useEffect(() => {
    console.log('[CLIENT] Component mounted');
    console.log('[CLIENT] Browser user agent:', navigator.userAgent);
    console.log('[CLIENT] Current URL:', window.location.href);
  }, []);
  
  const handleClick = () => {
    console.log('[CLIENT] Button clicked');
    console.log('[CLIENT] Product ID:', product.id);
    setIsLoading(true);
  };
  
  return (
    <div>
      <button onClick={handleClick}>Add to Cart</button>
    </div>
  );
}

Step 4: Universal Code Debugging

// Code that runs in both server and client contexts
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';

export default function MyComponent() {
  const { sitecoreContext } = useSitecoreContext();
  
  // This code runs both on server (during SSR) and client (during hydration)
  // Use different prefixes to distinguish
  const isServer = typeof window === 'undefined';
  const prefix = isServer ? '[SERVER]' : '[CLIENT]';
  
  console.log(`${prefix} Component rendering`);
  console.log(`${prefix} Page state:`, sitecoreContext.pageState);
  console.log(`${prefix} Language:`, sitecoreContext.language);
  
  return <div>{/* component */}</div>;
}

Advanced Console Debugging Techniques

Step 5: Environment-Conditional Logging

// utils/logger.js
const isDevelopment = process.env.NODE_ENV === 'development';
const isServer = typeof window === 'undefined';

export const logger = {
  debug: (message, ...args) => {
    if (isDevelopment) {
      const prefix = isServer ? '[SERVER]' : '[CLIENT]';
      console.log(`${prefix} DEBUG:`, message, ...args);
    }
  },
  
  info: (message, ...args) => {
    const prefix = isServer ? '[SERVER]' : '[CLIENT]';
    console.info(`${prefix} INFO:`, message, ...args);
  },
  
  error: (message, ...args) => {
    const prefix = isServer ? '[SERVER]' : '[CLIENT]';
    console.error(`${prefix} ERROR:`, message, ...args);
  },
  
  xmcloud: (message, ...args) => {
    if (isDevelopment) {
      const prefix = isServer ? '[SERVER]' : '[CLIENT]';
      console.log(`${prefix} XMCLOUD:`, message, ...args);
    }
  }
};

// Usage throughout your app
import { logger } from '../utils/logger';

export default function MyComponent({ fields }) {
  logger.debug('Component props received:', fields);
  logger.xmcloud('Sitecore fields:', fields);
  
  return <div>{/* component */}</div>;
}

Step 6: Structured Logging for XM Cloud

// utils/xmcloudLogger.js
const logXMCloudData = (context, data, operation = 'unknown') => {
  const isServer = typeof window === 'undefined';
  const prefix = isServer ? '[SERVER]' : '[CLIENT]';
  
  console.group(`${prefix} XM Cloud ${operation}`);
  console.log('Context:', context);
  console.log('Operation:', operation);
  console.log('Timestamp:', new Date().toISOString());
  console.log('Data:', data);
  console.groupEnd();
};

// Usage in components
export default function ProductGrid({ products }) {
  logXMCloudData('ProductGrid', products, 'component-render');
  
  return <div>{/* component */}</div>;
}

// Usage in API routes
export default async function handler(req, res) {
  const layoutData = await fetchLayoutFromXMCloud(req.query.path);
  logXMCloudData('Layout API', layoutData, 'layout-fetch');
  
  res.json(layoutData);
}

Step 7: Performance Timing with Console

// Track performance with console timing
export default function MyComponent() {
  console.time('[CLIENT] Component render time');
  
  useEffect(() => {
    console.timeEnd('[CLIENT] Component render time');
    
    // Track specific operations
    console.time('[CLIENT] Data processing');
    processData();
    console.timeEnd('[CLIENT] Data processing');
  }, []);
  
  return <div>{/* component */}</div>;
}

// In API routes
export default async function handler(req, res) {
  console.time('[SERVER] XM Cloud API call');
  
  const data = await fetchFromXMCloud();
  
  console.timeEnd('[SERVER] XM Cloud API call');
  res.json(data);
}

Step 8: Console Table for Better Data Visualization

// Display structured data clearly
export default function ProductListing({ products }) {
  // Use console.table for arrays and objects
  console.table(products.map(p => ({
    id: p.id,
    name: p.name,
    price: p.price,
    inStock: p.inventory > 0
  })));
  
  return <div>{/* component */}</div>;
}

// Debug Sitecore context data
export default function MyComponent() {
  const { sitecoreContext } = useSitecoreContext();
  
  console.table({
    'Page State': sitecoreContext.pageState,
    'Language': sitecoreContext.language,
    'Site Name': sitecoreContext.site?.name,
    'Item ID': sitecoreContext.route?.itemId,
    'Template ID': sitecoreContext.route?.templateId
  });
  
  return <div>{/* component */}</div>;
}

Practical XM Cloud Debugging Scenarios

Debug Layout Service Data Flow:

// In your layout service implementation
const fetchLayoutData = async (path) => {
  console.log('[SERVER] Fetching layout for path:', path);
  
  try {
    const response = await layoutService.fetchLayoutData(path);
    console.log('[SERVER] Layout response received');
    console.log('[SERVER] Response status:', response.sitecore?.context?.pageState);
    console.log('[SERVER] Component count:', response.sitecore?.route?.placeholders?.main?.length || 0);
    
    return response;
  } catch (error) {
    console.error('[SERVER] Layout fetch failed:', error.message);
    console.error('[SERVER] Error details:', error);
    throw error;
  }
};

Debug Component Rendering Issues:

export default function SitecoreComponent({ rendering, params }) {
  console.log('[CLIENT] Component rendering:', rendering.componentName);
  console.log('[CLIENT] Component params:', params);
  console.log('[CLIENT] Component fields:', rendering.fields);
  
  // Check for missing required fields
  const requiredFields = ['title', 'description'];
  requiredFields.forEach(field => {
    if (!rendering.fields?.[field]?.value) {
      console.warn(`[CLIENT] Missing required field: ${field}`);
    }
  });
  
  return <div>{/* component */}</div>;
}

6. Browser DevTools: Frontend Debugging

Browser DevTools provide powerful debugging capabilities specifically for your React components and XM Cloud integration. This is your primary tool for client-side debugging.

Step-by-Step Setup and Usage

Step 1: Essential Browser Extensions

Install these Chrome/Edge extensions:

# React Developer Tools
# https://chrome.google.com/webstore/detail/react-developer-tools/

# Redux DevTools (if using Redux)
# https://chrome.google.com/webstore/detail/redux-devtools/

Step 2: React DevTools for Sitecore Components

  1. Open DevTools (F12)
  2. Navigate to the “Components” tab (added by React DevTools)
  3. Inspect your Sitecore components:
// You can see these in React DevTools
export default function ProductCard({ rendering }) {
  const [isHovered, setIsHovered] = useState(false);
  
  // React DevTools shows:
  // - Component props (rendering object)
  // - Component state (isHovered)
  // - Component hooks
  
  return (
    <div 
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      {/* React DevTools shows current prop values */}
      <h3>{rendering.fields?.title?.value}</h3>
    </div>
  );
}

Debugging Sitecore Context in React DevTools:

  • Search for “SitecoreContext” component
  • Inspect the context value in props
  • See the entire sitecoreContext object structure

Step 3: Network Tab for XM Cloud API Debugging

Monitor your XM Cloud GraphQL requests:

  1. Open DevTools → Network tab
  2. Filter by “XHR” or “Fetch”
  3. Look for requests to your XM Cloud GraphQL endpoint
// You'll see these requests in Network tab
const fetchProductData = async (productId) => {
  // This will appear in Network tab
  const response = await fetch('/api/graphql', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `
        query GetProduct($id: String!) {
          product(id: $id) {
            name
            price
            description
          }
        }
      `,
      variables: { id: productId }
    })
  });
  
  return response.json();
};

What to Look For in Network Tab:

  • Request URL – Verify correct GraphQL endpoint
  • Request Headers – Check authentication tokens
  • Request Payload – Inspect GraphQL query and variables
  • Response – See actual data returned from XM Cloud
  • Timing – Identify slow requests

Step 4: Console Tab for Runtime Debugging

Use the Console tab to interact with your running application:

// Type these directly in the Console tab:

// Access React components
$r // Currently selected React component in DevTools

// Access global variables
window.__NEXT_DATA__ // Next.js page data
window.sitecoreContext // If exposed globally

// Test functions
myGlobalFunction('test') // Call global functions

// Inspect DOM elements
$0 // Currently selected DOM element
document.querySelector('[data-testid="product-card"]')

// Debug Sitecore fields
console.log($r.props.rendering.fields) // If component selected

Step 5: Performance Profiler

Debug rendering performance issues:

  1. Open DevTools → Profiler tab (React DevTools)
  2. Click “Start profiling”
  3. Interact with your app (navigate, click buttons)
  4. Click “Stop profiling”
// Optimize components based on profiler results
import { memo, useMemo } from 'react';

const ProductList = memo(({ products }) => {
  // Use useMemo for expensive calculations
  const sortedProducts = useMemo(() => {
    return products.sort((a, b) => a.name.localeCompare(b.name));
  }, [products]);
  
  return (
    <div>
      {sortedProducts.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
});

Step 6: Application Tab for Storage Debugging

Check browser storage used by your XM Cloud app:

  • Local Storage – Persistent data
  • Session Storage – Session-specific data
  • Cookies – Authentication tokens, preferences
  • IndexedDB – Large data storage
// Debug storage issues in Console
localStorage.getItem('user-preferences')
sessionStorage.getItem('cart-items')
document.cookie

// Clear storage for testing
localStorage.clear()
sessionStorage.clear()

Advanced DevTools Techniques

Step 7: Sources Tab for Advanced Debugging

  1. Navigate to Sources tab
  2. Find your source files
  3. Set breakpoints by clicking line numbers
  4. Use conditional breakpoints (right-click → Add conditional breakpoint)
// Example: Conditional breakpoint
// Right-click line number, add condition: product.price > 100
export default function ProductCard({ product }) {
  const handleClick = () => {
    // Breakpoint here with condition: product.price > 100
    analytics.track('product_clicked', product);
  };
  
  return <div onClick={handleClick}>{product.name}</div>;
}

Step 8: Memory Tab for Performance Issues

Debug memory leaks in your XM Cloud app:

  1. Open DevTools → Memory tab
  2. Take heap snapshots
  3. Compare snapshots to find memory leaks
// Common memory leak patterns to watch for
export default function ComponentWithLeak() {
  useEffect(() => {
    const interval = setInterval(() => {
      // This creates a memory leak if not cleaned up
      fetchData();
    }, 1000);
    
    // Fix: Clean up interval
    return () => clearInterval(interval);
  }, []);
  
  return <div>Component</div>;
}

XM Cloud-Specific DevTools Usage

Debug Experience Editor Integration:

// Check if in Experience Editor
const isEditing = window.top !== window.self;
console.log('In Experience Editor:', isEditing);

// Inspect Experience Editor messages
window.addEventListener('message', (event) => {
  console.log('Experience Editor message:', event.data);
});

Debug Layout Service Responses:

// In Network tab, find layout service requests
// Right-click → Copy → Copy as cURL
// Paste in terminal to replay the exact request

// Or in Console tab:
fetch('/api/layout?path=/products')
  .then(r => r.json())
  .then(data => console.table(data.sitecore.route.placeholders));

Monitor Component Field Updates:

// Add this to components to monitor field changes
export default function MyComponent({ rendering }) {
  useEffect(() => {
    console.log('Fields updated:', rendering.fields);
  }, [rendering.fields]);
  
  return <div>{/* component */}</div>;
}

7. XM Cloud-Specific Debugging Techniques

These debugging approaches are specifically tailored for common XM Cloud development scenarios and challenges.

Step-by-Step XM Cloud Debugging Setup

Step 1: Debugging Sitecore Context

Create a dedicated debug component to inspect Sitecore context:

// components/debug/SitecoreContextDebugger.jsx
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';
import { useState } from 'react';

export default function SitecoreContextDebugger() {
  const { sitecoreContext } = useSitecoreContext();
  const [showDebugger, setShowDebugger] = useState(false);
  
  // Only show in development
  if (process.env.NODE_ENV !== 'development') {
    return null;
  }
  
  const toggleDebugger = () => setShowDebugger(!showDebugger);
  
  return (
    <div style={{ 
      position: 'fixed', 
      bottom: '10px', 
      right: '10px', 
      zIndex: 9999,
      backgroundColor: '#1e1e1e',
      color: '#fff',
      padding: '10px',
      borderRadius: '5px',
      fontFamily: 'monospace',
      fontSize: '12px'
    }}>
      <button onClick={toggleDebugger} style={{ marginBottom: '10px' }}>
        {showDebugger ? 'Hide' : 'Show'} Sitecore Debug
      </button>
      
      {showDebugger && (
        <div style={{ maxWidth: '400px', maxHeight: '300px', overflow: 'auto' }}>
          <h4>Sitecore Context</h4>
          <p><strong>Page State:</strong> {sitecoreContext.pageState}</p>
          <p><strong>Language:</strong> {sitecoreContext.language}</p>
          <p><strong>Site:</strong> {sitecoreContext.site?.name}</p>
          <p><strong>Item ID:</strong> {sitecoreContext.route?.itemId}</p>
          <p><strong>Template:</strong> {sitecoreContext.route?.templateName}</p>
          <p><strong>Path:</strong> {sitecoreContext.route?.name}</p>
          
          <details>
            <summary>Full Context</summary>
            <pre style={{ fontSize: '10px', whiteSpace: 'pre-wrap' }}>
              {JSON.stringify(sitecoreContext, null, 2)}
            </pre>
          </details>
        </div>
      )}
    </div>
  );
}

Add to your Layout component:

// src/Layout.jsx
import SitecoreContextDebugger from './components/debug/SitecoreContextDebugger';

export default function Layout({ layoutData }) {
  return (
    <div>
      {/* Your layout content */}
      <SitecoreContextDebugger />
    </div>
  );
}

Step 2: Debugging GraphQL Queries

Create a GraphQL debug wrapper:

// lib/graphql-debug.js
import { GraphQLRequestClient } from '@sitecore-jss/sitecore-jss-nextjs';

class DebugGraphQLClient extends GraphQLRequestClient {
  async request(query, variables) {
    const isDebug = process.env.DEBUG_GRAPHQL === 'true';
    
    if (isDebug) {
      console.group('🚀 GraphQL Request');
      console.log('Query:', query);
      console.log('Variables:', variables);
      console.log('Endpoint:', this.endpoint);
      console.time('GraphQL Response Time');
    }
    
    try {
      const result = await super.request(query, variables);
      
      if (isDebug) {
        console.timeEnd('GraphQL Response Time');
        console.log('✅ Response:', result);
        console.groupEnd();
      }
      
      return result;
    } catch (error) {
      if (isDebug) {
        console.timeEnd('GraphQL Response Time');
        console.error('❌ GraphQL Error:', error);
        console.groupEnd();
      }
      throw error;
    }
  }
}

export const debugGraphQLClient = new DebugGraphQLClient(
  process.env.SITECORE_API_HOST,
  {
    apiKey: process.env.SITECORE_API_KEY,
  }
);

Use in your GraphQL queries:

// Enable GraphQL debugging
// Set DEBUG_GRAPHQL=true in your .env.local file

// lib/layout-service.js
import { debugGraphQLClient } from './graphql-debug';

export const layoutService = {
  async fetchLayoutData(path) {
    const query = `
      query LayoutQuery($path: String!) {
        layout(routePath: $path) {
          item {
            id
            name
            path
            fields {
              name
              value
            }
          }
        }
      }
    `;
    
    // This will now log detailed debug info
    return await debugGraphQLClient.request(query, { path });
  }
};

Step 3: Debugging Experience Editor Integration

Create an Experience Editor debug helper:

// components/debug/ExperienceEditorDebugger.jsx
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';
import { useEffect, useState } from 'react';

export default function ExperienceEditorDebugger() {
  const { sitecoreContext } = useSitecoreContext();
  const [messages, setMessages] = useState([]);
  const isEditing = sitecoreContext.pageState === 'edit';
  
  useEffect(() => {
    if (!isEditing) return;
    
    const handleMessage = (event) => {
      setMessages(prev => [...prev.slice(-9), {
        timestamp: new Date().toISOString(),
        data: event.data,
        origin: event.origin
      }]);
    };
    
    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, [isEditing]);
  
  if (!isEditing || process.env.NODE_ENV !== 'development') {
    return null;
  }
  
  return (
    <div style={{
      position: 'fixed',
      top: '10px',
      left: '10px',
      backgroundColor: '#fff3cd',
      border: '1px solid #ffeaa7',
      padding: '10px',
      borderRadius: '5px',
      maxWidth: '300px',
      fontSize: '12px',
      zIndex: 9999
    }}>
      <h4>🎯 Experience Editor Debug</h4>
      <p><strong>Page State:</strong> {sitecoreContext.pageState}</p>
      <p><strong>Item ID:</strong> {sitecoreContext.route?.itemId}</p>
      
      <details>
        <summary>Recent Messages ({messages.length})</summary>
        <div style={{ maxHeight: '200px', overflow: 'auto' }}>
          {messages.map((msg, i) => (
            <div key={i} style={{ borderBottom: '1px solid #ddd', padding: '5px 0' }}>
              <small>{msg.timestamp}</small>
              <pre style={{ fontSize: '10px' }}>{JSON.stringify(msg.data, null, 2)}</pre>
            </div>
          ))}
        </div>
      </details>
    </div>
  );
}

Step 4: Component Field Debugging

Create a field debugger for components:

// components/debug/FieldDebugger.jsx
export default function FieldDebugger({ fields, componentName }) {
  if (process.env.NODE_ENV !== 'development') {
    return null;
  }
  
  const hasEmptyFields = Object.entries(fields || {}).some(([key, field]) => !field?.value);
  
  return (
    <div style={{
      border: '2px dashed #ff6b6b',
      padding: '10px',
      margin: '5px 0',
      backgroundColor: '#ffe0e0',
      fontSize: '12px'
    }}>
      <h5>🔍 {componentName} Fields Debug</h5>
      {hasEmptyFields && <p style={{ color: 'red' }}>⚠️ Some fields are empty!</p>}
      
      <details>
        <summary>Fields ({Object.keys(fields || {}).length})</summary>
        {Object.entries(fields || {}).map(([key, field]) => (
          <div key={key} style={{ margin: '5px 0' }}>
            <strong>{key}:</strong> 
            {field?.value ? (
              <span style={{ color: 'green' }}>✓ {String(field.value).substring(0, 50)}</span>
            ) : (
              <span style={{ color: 'red' }}>✗ Empty</span>
            )}
          </div>
        ))}
      </details>
      
      <details>
        <summary>Raw Field Data</summary>
        <pre style={{ fontSize: '10px', whiteSpace: 'pre-wrap' }}>
          {JSON.stringify(fields, null, 2)}
        </pre>
      </details>
    </div>
  );
}

// Usage in your components
import FieldDebugger from './debug/FieldDebugger';

export default function ProductCard({ rendering }) {
  const { fields } = rendering;
  
  return (
    <div>
      <FieldDebugger fields={fields} componentName="ProductCard" />
      
      <h3>{fields?.title?.value}</h3>
      <p>{fields?.description?.value}</p>
    </div>
  );
}

Step 5: API Route Debugging for XM Cloud

Create comprehensive API debugging:

// pages/api/debug/layout.js
export default async function handler(req, res) {
  const { path = '/' } = req.query;
  
  try {
    console.log('🔍 Layout Debug API called');
    console.log('Path:', path);
    console.log('Headers:', req.headers);
    
    // Fetch layout data
    const layoutData = await layoutService.fetchLayoutData(path);
    
    // Create debug response
    const debugInfo = {
      request: {
        path,
        timestamp: new Date().toISOString(),
        userAgent: req.headers['user-agent'],
        referer: req.headers.referer
      },
      response: {
        hasLayout: !!layoutData?.sitecore?.route,
        itemId: layoutData?.sitecore?.route?.itemId,
        templateId: layoutData?.sitecore?.route?.templateId,
        componentCount: layoutData?.sitecore?.route?.placeholders?.main?.length || 0,
        language: layoutData?.sitecore?.context?.language,
        site: layoutData?.sitecore?.context?.site?.name
      },
      components: layoutData?.sitecore?.route?.placeholders?.main?.map(comp => ({
        componentName: comp.componentName,
        hasFields: Object.keys(comp.fields || {}).length > 0,
        fieldCount: Object.keys(comp.fields || {}).length
      })) || [],
      fullData: layoutData
    };
    
    res.json(debugInfo);
  } catch (error) {
    console.error('Layout debug error:', error);
    res.status(500).json({
      error: error.message,
      stack: error.stack
    });
  }
}

Step 6: Environment-Specific Debug Configuration

Create different debug configurations for different environments:

// lib/debug-config.js
const debugConfig = {
  development: {
    showSitecoreContext: true,
    showFieldDebugger: true,
    showPerformanceMetrics: true,
    logGraphQLQueries: true,
    showExperienceEditorDebug: true
  },
  staging: {
    showSitecoreContext: false,
    showFieldDebugger: false,
    showPerformanceMetrics: true,
    logGraphQLQueries: true,
    showExperienceEditorDebug: false
  },
  production: {
    showSitecoreContext: false,
    showFieldDebugger: false,
    showPerformanceMetrics: false,
    logGraphQLQueries: false,
    showExperienceEditorDebug: false
  }
};

export const getDebugConfig = () => {
  const env = process.env.NODE_ENV || 'development';
  return debugConfig[env] || debugConfig.development;
};

// Usage in components
import { getDebugConfig } from '../lib/debug-config';

export default function MyComponent() {
  const debug = getDebugConfig();
  
  return (
    <div>
      {/* Your component */}
      {debug.showFieldDebugger && <FieldDebugger />}
    </div>
  );
}

Step 7: Performance Debugging for XM Cloud

Create performance monitoring:

// lib/performance-debug.js
export const performanceDebugger = {
  measureComponent: (componentName) => {
    const start = performance.now();
    
    return {
      end: () => {
        const duration = performance.now() - start;
        if (duration > 100) { // Log slow components
          console.warn(`🐌 Slow component: ${componentName} took ${duration.toFixed(2)}ms`);
        } else {
          console.log(`⚡ ${componentName} rendered in ${duration.toFixed(2)}ms`);
        }
      }
    };
  },
  
  measureAPI: async (apiName, apiCall) => {
    const start = performance.now();
    try {
      const result = await apiCall();
      const duration = performance.now() - start;
      console.log(`🚀 API ${apiName} completed in ${duration.toFixed(2)}ms`);
      return result;
    } catch (error) {
      const duration = performance.now() - start;
      console.error(`💥 API ${apiName} failed after ${duration.toFixed(2)}ms:`, error);
      throw error;
    }
  }
};

// Usage in components
import { performanceDebugger } from '../lib/performance-debug';

export default function ProductGrid({ products }) {
  const perf = performanceDebugger.measureComponent('ProductGrid');
  
  useEffect(() => {
    perf.end();
  });
  
  return <div>{/* component */}</div>;
}

Best Practices for XM Cloud Debugging

  1. Use appropriate debugging levels – Don’t enable all debug logs in production
  2. Combine multiple approaches – Use breakpoints for logic, debug logs for data flow
  3. Understand server vs client execution – Know where your code runs
  4. Debug GraphQL queries – Always verify your XM Cloud data queries
  5. Monitor Experience Editor integration – Test your components in editing mode
  6. Use conditional debugging – Only debug when specific conditions are met
  7. Create reusable debug components – Build debug utilities you can use across projects
  8. Environment-aware debugging – Different debug levels for different environments

Conclusion

These advanced debugging strategies complete your XM Cloud debugging toolkit:

Console-Based Debugging helps you understand the server vs client execution context and provides structured logging approaches for complex data flows.

Browser DevTools give you deep insight into your React components, network requests, and performance characteristics with professional-grade debugging tools.

XM Cloud-Specific Techniques provide targeted debugging approaches for Sitecore-specific challenges like Experience Editor integration, GraphQL queries, and component field debugging.

Combined with the essential techniques from Part 1, you now have a comprehensive debugging arsenal that surpasses what was available in traditional MVC development. The key to debugging mastery is knowing which technique to use for each scenario and how to combine them effectively.

Whether you’re debugging a complex component hierarchy, troubleshooting GraphQL queries, investigating Experience Editor issues, or optimizing performance, these tools will help you diagnose and fix problems quickly and efficiently.

Remember: effective debugging is about using the right tool for the job. Start with the simplest approach that gives you the information you need, and escalate to more advanced techniques as necessary.

Happy debugging, and welcome to the future of Sitecore development!

Leave a comment