Part 1: Essential Debugging Techniques for Sitecore XM Cloud Next.js Apps

As Sitecore developers transition from traditional MVC development to headless Next.js applications with XM Cloud, one of the most common questions is: “How do I debug my application like I used to in Visual Studio?”

The good news is that modern Next.js development offers even more powerful debugging capabilities than traditional server-side development. This two-part series will equip you with comprehensive debugging strategies specifically tailored for Sitecore XM Cloud development.

Part 1 (this post) covers the essential debugging techniques that form the foundation of your development workflow:

  • VS Code Integrated Debugging
  • Sitecore JSS Debug Logging
  • Custom Debug Logging
  • Programmatic Debugging

Part 2 will dive into advanced debugging strategies including browser DevTools, console debugging patterns, and XM Cloud-specific debugging techniques.

Let’s start with the core debugging approaches every XM Cloud developer needs to master.

1. VS Code Integrated Debugging: Your New Visual Studio

Coming from Visual Studio, you’ll feel right at home with VS Code’s debugging capabilities. You can set breakpoints, step through code, and inspect variables just like you’re used to.

Step-by-Step Setup

Step 1: Create Debug Configuration

Create a .vscode folder in your project root if it doesn’t exist, then create a launch.json file inside it:

json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Next.js: debug server-side",
      "type": "node-terminal",
      "request": "launch",
      "command": "npm run dev",
      "console": "integratedTerminal"
    },
    {
      "name": "Next.js: debug client-side",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}"
    },
    {
      "name": "Next.js: debug full stack",
      "type": "node-terminal",
      "request": "launch",
      "command": "npm run dev",
      "console": "integratedTerminal",
      "serverReadyAction": {
        "pattern": "- Local:.+(https?://.+)",
        "uriFormat": "%s",
        "action": "debugWithChrome"
      }
    }
  ]
}

Step 2: Set Your First Breakpoints

Click in the left margin (line numbers area) to set breakpoints in your code:

javascript

// pages/api/sitecore/layout.js
export default async function handler(req, res) {
  const { path } = req.query;
  // Set breakpoint on this line ← Click here in VS Code
  const layoutData = await layoutService.fetchLayoutData(path);
  
  // Set another breakpoint here to inspect the response
  res.json(layoutData);
}

Step 3: Start Debugging

  1. Open the Debug panel in VS Code (Ctrl+Shift+D)
  2. Select your debug configuration from the dropdown
  3. Press F5 or click the green play button
  4. VS Code will start your Next.js app and attach the debugger

Step 4: Debug Your XM Cloud Integration

Once running, navigate to a page that triggers your breakpoints. VS Code will pause execution and show:

  • Variables panel – Inspect layoutData, req, res objects
  • Call Stack – See the execution path
  • Debug Console – Execute JavaScript expressions
  • Watch panel – Monitor specific variables

Advanced VS Code Debugging Features

Conditional Breakpoints: Right-click on a breakpoint to set conditions:

javascript

// Only pause when debugging specific Sitecore items
if (layoutData.sitecore.route.itemId === '{YOUR-ITEM-ID}') {
  // This breakpoint will only trigger for this item
}

Logpoints (Non-breaking breakpoints): Right-click in the margin and select “Add Logpoint” to log without stopping execution.

What You Can Debug

  • Server-side rendering (SSR) – Debug getServerSideProps and getStaticProps
  • API routes – Step through your custom API endpoints
  • Middleware – Debug authentication, redirects, and custom logic
  • Component rendering – Inspect React component lifecycle and state
  • Sitecore JSS integration – Debug layout service calls and data fetching

2. Sitecore JSS Debug Logging: Understanding the Framework

Sitecore JSS uses the debug npm package internally, giving you deep visibility into framework operations. This is invaluable for understanding what’s happening behind the scenes with your XM Cloud integration.

Step-by-Step Setup

Step 1: Understanding the Debug Package

The debug package is already included in your JSS project. No additional installation needed! It works by reading environment variables to determine what to log.

Step 2: Enable JSS Debug Logging

Open your terminal and run your Next.js app with debug flags:

bash

# Windows (Command Prompt)
set DEBUG=sitecore-jss:* && npm run dev

# Windows (PowerShell)
$env:DEBUG="sitecore-jss:*"; npm run dev

# macOS/Linux
DEBUG=sitecore-jss:* npm run dev

Step 3: Add Debug Scripts to package.json

Make debugging easier by adding these scripts:

json

{
  "scripts": {
    "dev": "next dev",
    "dev:debug": "DEBUG=sitecore-jss:* npm run dev",
    "dev:debug-layout": "DEBUG=sitecore-jss:layout npm run dev",
    "dev:debug-http": "DEBUG=sitecore-jss:http npm run dev",
    "dev:debug-editing": "DEBUG=sitecore-jss:editing npm run dev"
  }
}

Now you can run: npm run dev:debug-layout

Essential JSS Debug Namespaces

Layout Service Debugging:

bash

DEBUG=sitecore-jss:layout npm run dev

What you’ll see: Layout service requests, response data, route resolution Use when: Components aren’t rendering, missing data, routing issues

HTTP Request Debugging:

bash

DEBUG=sitecore-jss:http npm run dev

What you’ll see: GraphQL queries, HTTP requests/responses, API calls Use when: Slow loading, failed requests, GraphQL query issues

Experience Editor Debugging:

bash

DEBUG=sitecore-jss:editing npm run dev

What you’ll see: Editor integration, preview mode, editing context Use when: Experience Editor issues, preview not working

Multiple Namespaces:

bash

DEBUG=sitecore-jss:layout,sitecore-jss:http npm run dev

Advanced Debug Configuration

Environment Variables for Better Output:

bash

# Pretty-print objects on multiple lines
DEBUG_MULTILINE=true DEBUG=sitecore-jss:layout npm run dev

# Increase object inspection depth
DEBUG_DEPTH=4 DEBUG=sitecore-jss:* npm run dev

# Hide timestamps for cleaner output
DEBUG_HIDE_DATE=true DEBUG=sitecore-jss:* npm run dev

# Disable colors (useful for log files)
DEBUG_COLORS=false DEBUG=sitecore-jss:* npm run dev

Reading Debug Output

When you enable debug logging, you’ll see output like this:

sitecore-jss:layout Fetching layout data for path: /products +0ms
sitecore-jss:http POST https://your-xmcloud-instance.sitecorecloud.io/api/graphql +5ms
sitecore-jss:http GraphQL query: query LayoutQuery($path: String!) { ... } +2ms
sitecore-jss:layout Layout data received: { sitecore: { context: { ... } } } +125ms

Understanding the Format:

  • sitecore-jss:layout – The namespace
  • Fetching layout data... – The debug message
  • +125ms – Time since last debug message in this namespace

3. Custom Debug Logging: Your Application Logic

Implement your own debug logging alongside JSS’s built-in logging to track your custom application behavior.

Step-by-Step Implementation

Step 1: Install debug package (if not already available)

bash

npm install debug

Step 2: Create a debug utility module

Create src/utils/debug.js:

javascript

const debug = require('debug');

// Create different debug channels for different parts of your app
export const componentDebug = debug('myapp:components');
export const dataDebug = debug('myapp:data');
export const authDebug = debug('myapp:auth');
export const personalizationDebug = debug('myapp:personalization');
export const performanceDebug = debug('myapp:performance');

// Helper function for conditional debugging
export const debugIf = (condition, debugFn, message, ...args) => {
  if (condition) {
    debugFn(message, ...args);
  }
};

Step 3: Use in your components

javascript

// components/ProductListing.jsx
import { componentDebug, dataDebug } from '../utils/debug';
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';

export default function ProductListing({ fields }) {
  const { sitecoreContext } = useSitecoreContext();
  
  componentDebug('ProductListing rendering with fields: %o', fields);
  
  useEffect(() => {
    componentDebug('ProductListing mounted');
    dataDebug('Sitecore context: %o', sitecoreContext);
  }, []);
  
  const handleProductClick = (productId) => {
    componentDebug('Product clicked: %s', productId);
    // Your click logic here
  };
  
  return (
    <div>
      {/* Your component JSX */}
    </div>
  );
}

Step 4: Use in API routes

javascript

// pages/api/products/[id].js
import { dataDebug, performanceDebug } from '../../../utils/debug';

export default async function handler(req, res) {
  const start = Date.now();
  const { id } = req.query;
  
  dataDebug('API called for product ID: %s', id);
  dataDebug('Request method: %s', req.method);
  dataDebug('Request headers: %o', req.headers);
  
  try {
    // Your API logic here
    const product = await fetchProductFromXMCloud(id);
    
    const duration = Date.now() - start;
    performanceDebug('API call completed in %dms', duration);
    
    res.json(product);
  } catch (error) {
    dataDebug('API error: %o', error);
    res.status(500).json({ error: 'Failed to fetch product' });
  }
}

Step 5: Enable your custom debug logs

bash

# Enable only your app's debug logs
DEBUG=myapp:* npm run dev

# Enable both JSS and your app's logs
DEBUG=sitecore-jss:*,myapp:* npm run dev

# Enable specific channels only
DEBUG=myapp:components,myapp:data npm run dev

# Exclude certain channels
DEBUG=myapp:*,-myapp:performance npm run dev

Advanced Custom Debug Patterns

Performance Monitoring:

javascript

// utils/debug.js
export const createPerformanceDebugger = (namespace) => {
  const debug = require('debug')(namespace);
  
  return {
    start: (operation) => {
      const start = Date.now();
      debug(`Starting ${operation}`);
      return () => {
        const duration = Date.now() - start;
        debug(`${operation} completed in ${duration}ms`);
      };
    }
  };
};

// Usage in components
import { createPerformanceDebugger } from '../utils/debug';
const perfDebug = createPerformanceDebugger('myapp:performance');

export default function MyComponent() {
  const endTimer = perfDebug.start('component-render');
  
  // Your component logic
  
  useEffect(() => {
    endTimer(); // Logs: "component-render completed in 15ms"
  }, []);
}

Conditional Debug Based on Sitecore Context:

javascript

// utils/sitecoreDebug.js
import { debugIf } from './debug';
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';

export const useSitecoreDebug = () => {
  const { sitecoreContext } = useSitecoreContext();
  const isEditing = sitecoreContext.pageState === 'edit';
  const isPreview = sitecoreContext.pageState === 'preview';
  
  return {
    debugInEditor: (debugFn, message, ...args) => 
      debugIf(isEditing, debugFn, `[EDITOR] ${message}`, ...args),
    debugInPreview: (debugFn, message, ...args) => 
      debugIf(isPreview, debugFn, `[PREVIEW] ${message}`, ...args),
    debugInNormal: (debugFn, message, ...args) => 
      debugIf(!isEditing && !isPreview, debugFn, `[NORMAL] ${message}`, ...args)
  };
};

Debug Package Scripts for Different Scenarios:

json

{
  "scripts": {
    "dev": "next dev",
    "dev:debug-all": "DEBUG=sitecore-jss:*,myapp:* npm run dev",
    "dev:debug-components": "DEBUG=myapp:components npm run dev",
    "dev:debug-data": "DEBUG=sitecore-jss:layout,myapp:data npm run dev",
    "dev:debug-performance": "DEBUG=myapp:performance npm run dev",
    "dev:debug-editor": "DEBUG=sitecore-jss:editing,myapp:components npm run dev"
  }
}

Combining with JSS logging

bash

# The power combination - see both framework and app behavior
DEBUG=sitecore-jss:layout,sitecore-jss:http,myapp:components,myapp:data npm run dev

This gives you complete visibility into both what Sitecore JSS is doing internally and how your custom application logic is behaving.

4. Programmatic Debugging: Strategic Breakpoints

Use debugger; statements for targeted debugging in complex scenarios where you need to pause execution programmatically.

Step-by-Step Implementation

Step 1: Understanding the debugger keyword

The debugger; statement acts like a breakpoint written directly in your code. When the browser’s developer tools are open, JavaScript execution will pause at this line.

Step 2: Basic Usage

javascript

// In any JavaScript file
function processData(data) {
  console.log('Processing data...');
  debugger; // Execution pauses here when dev tools are open
  const result = data.map(item => item.value);
  return result;
}

Step 3: Conditional Debugging in XM Cloud Scenarios

Debug Only in Experience Editor:

javascript

import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';

export default function MyComponent({ fields }) {
  const { sitecoreContext } = useSitecoreContext();
  
  // Only pause when in Experience Editor
  if (sitecoreContext.pageState === 'edit') {
    debugger; // Inspect component state in editor mode
  }
  
  return <div>{/* component */}</div>;
}

Debug Specific Content Items:

javascript

export default function ProductDetail({ params }) {
  const { sitecoreContext } = useSitecoreContext();
  const route = sitecoreContext?.route;
  
  // Debug only problematic items
  if (route?.itemId === '{PROBLEMATIC-ITEM-ID}') {
    debugger; // Pause for this specific item
  }
  
  return <div>{/* component */}</div>;
}

Debug API Routes Conditionally:

javascript

// pages/api/sitecore/layout.js
export default async function handler(req, res) {
  const { path } = req.query;
  
  // Debug specific paths
  if (path && path.includes('/products/problem-product')) {
    debugger; // Pause for problematic routes
  }
  
  // Debug based on request method
  if (req.method === 'POST') {
    debugger; // Pause for POST requests
  }
  
  const layoutData = await layoutService.fetchLayoutData(path);
  res.json(layoutData);
}

Advanced Debugging Patterns

Step 4: Debug in Loops and Iterations

javascript

// Debug specific iterations
export default function ProductGrid({ products }) {
  return (
    <div>
      {products.map((product, index) => {
        // Debug the 5th product
        if (index === 4) {
          debugger; // Inspect this specific product
        }
        
        // Debug products with missing data
        if (!product.name || !product.price) {
          debugger; // Pause for incomplete data
        }
        
        return <ProductCard key={product.id} product={product} />;
      })}
    </div>
  );
}

Debug Based on User Behavior:

javascript

export default function SearchResults({ query }) {
  const handleSearch = (searchTerm) => {
    // Debug specific search terms
    if (searchTerm.toLowerCase().includes('debug')) {
      debugger; // Pause for debug searches
    }
    
    // Debug empty searches
    if (!searchTerm.trim()) {
      debugger; // Investigate empty search behavior
    }
    
    performSearch(searchTerm);
  };
  
  return <SearchComponent onSearch={handleSearch} />;
}

Step 5: Environment-Aware Debugging

javascript

// Only debug in development
const isDevelopment = process.env.NODE_ENV === 'development';

export default function MyComponent({ data }) {
  if (isDevelopment && !data) {
    debugger; // Only pause in development for missing data
  }
  
  return <div>{/* component */}</div>;
}

Debug with Feature Flags:

javascript

// Use environment variables for debug control
const DEBUG_COMPONENTS = process.env.DEBUG_COMPONENTS === 'true';

export default function MyComponent({ fields }) {
  if (DEBUG_COMPONENTS) {
    debugger; // Controlled by environment variable
  }
  
  return <div>{/* component */}</div>;
}

Step 6: Using debugger; with Dev Tools

How to Use:

  1. Open your browser’s Developer Tools (F12)
  2. Navigate to your page or trigger the code path
  3. When JavaScript hits a debugger; statement, execution pauses
  4. Use the debugging controls:
    • Continue (F8) – Resume execution
    • Step Over (F10) – Execute next line
    • Step Into (F11) – Step into function calls
    • Step Out (Shift+F11) – Step out of current function

Inspect Variables:

  • Hover over variables to see their values
  • Use the Console tab to evaluate expressions
  • Check the Scope panel to see all available variables

Debugging XM Cloud GraphQL Responses

javascript

// In your GraphQL client or data fetching logic
const fetchLayoutData = async (path) => {
  try {
    const response = await graphQLClient.request(layoutQuery, { path });
    
    // Debug unexpected response structure
    if (!response.layout || !response.layout.item) {
      debugger; // Pause to inspect malformed response
    }
    
    return response;
  } catch (error) {
    debugger; // Pause on GraphQL errors
    throw error;
  }
};

Best Practices

  1. Remove before production – Always remove debugger; statements before deploying
  2. Use conditionally – Don’t pause on every execution, only when specific conditions are met
  3. Combine with console.log – Use both logging and pausing for complete picture
  4. Document your debugging – Comment why you’re using debugger statements

javascript

// Good practice: Comment your debugging intent
export default function MyComponent({ data }) {
  // TODO: Remove after fixing data loading issue
  if (!data.requiredField) {
    debugger; // Investigating missing requiredField
  }
  
  return <div>{/* component */}</div>;
}

Conclusion

These four essential debugging techniques provide the foundation for effective XM Cloud development:

  1. VS Code Integrated Debugging gives you the familiar Visual Studio-like debugging experience with breakpoints and variable inspection
  2. Sitecore JSS Debug Logging provides deep visibility into the framework’s internal operations
  3. Custom Debug Logging allows you to track your application logic with organized, conditional logging
  4. Programmatic Debugging offers strategic breakpoints for complex scenarios

With these techniques in your toolkit, you can debug server-side rendering, API routes, component logic, and Sitecore integration issues effectively. The key is knowing when to use each approach and how to combine them for maximum debugging power.

Coming Up in Part 2: We’ll explore advanced debugging strategies including console debugging patterns, browser DevTools mastery, and XM Cloud-specific debugging techniques that will take your debugging skills to the next level.

Happy debugging!

One response to “Part 1: Essential Debugging Techniques for Sitecore XM Cloud Next.js Apps”

Leave a comment