Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .agent/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ Subagents provide specialized capabilities. Read them when tasks require domain
| `tools/ai-assistants/` | AI tool integration - configuring assistants, CAPTCHA solving, multi-modal agents | agno, capsolver, windsurf, configuration, status |
| `tools/ai-orchestration/` | AI orchestration frameworks - visual builders, multi-agent teams, workflow automation, DSL orchestration | overview, langflow, crewai, autogen, openprose, packaging |
| `tools/browser/` | Browser automation - web scraping, testing, screenshots, form filling | agent-browser, stagehand, playwright, playwriter, crawl4ai, dev-browser, pagespeed, chrome-devtools |
| `tools/ui/` | UI components - component libraries, design systems, interface constraints | shadcn, ui-skills |
| `tools/ui/` | UI components - component libraries, design systems, frontend debugging, hydration errors | shadcn, ui-skills, frontend-debugging |
| `tools/code-review/` | Code quality - linting, security scanning, style enforcement, PR reviews | code-standards, code-simplifier, codacy, coderabbit, qlty, snyk, secretlint, auditing |
| `tools/context/` | Context optimization - semantic search, codebase indexing, token efficiency | osgrep, augment-context-engine, context-builder, context7, toon, dspy, llm-tldr |
| `tools/conversion/` | Format conversion - document transformation between formats | pandoc |
Expand Down Expand Up @@ -457,6 +457,7 @@ For AI-assisted setup guidance, see `aidevops/setup.md`.
| Git operations | `workflows/git-workflow.md`, `tools/git/github-cli.md` |
| Release/versioning | `workflows/release.md`, `workflows/version-bump.md` |
| Browser automation | `tools/browser/stagehand.md` or `tools/browser/playwright.md` |
| Frontend debugging | `tools/ui/frontend-debugging.md` (hydration errors, monorepo gotchas) |
| WordPress work | `tools/wordpress/wp-dev.md`, `tools/wordpress/mainwp.md` |
| SEO analysis | `seo/dataforseo.md`, `seo/google-search-console.md` |
| Sitemap submission | `seo/gsc-sitemaps.md` |
Expand Down
284 changes: 284 additions & 0 deletions .agent/tools/ui/frontend-debugging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
---
description: Frontend debugging patterns - browser verification, hydration errors, monorepo gotchas
mode: subagent
tools:
read: true
write: false
edit: false
bash: true
glob: true
grep: true
webfetch: false
task: true
---

# Frontend Debugging Guide

<!-- AI-CONTEXT-START -->

## Quick Reference

- **Golden Rule**: Always verify frontend fixes with browser screenshot, never trust curl alone
- **Hydration errors**: Usually mean server/client mismatch or invalid component types
- **Monorepo gotchas**: Webpack loaders (SVGR, etc.) don't cross package boundaries
- **Browser tool**: `dev-browser` agent for visual verification

**When to read this guide**:
- Debugging React/Next.js errors
- "Something went wrong" or blank page issues
- Hydration mismatch errors
- Working in monorepo `packages/` directories
- After curl returns 200 but user reports errors

<!-- AI-CONTEXT-END -->

## The Browser Verification Rule

**CRITICAL**: HTTP status codes and HTML responses do NOT verify frontend functionality.

### Why curl Lies

```bash
# This returns 200 OK even when React crashes client-side:
curl -s https://myapp.local -o /dev/null -w "%{http_code}"
# Output: 200

# The HTML contains the error boundary, not the actual app:
curl -s https://myapp.local | grep -o "Something went wrong"
# Output: Something went wrong
```

**The server returns 200 because**:
- Next.js SSR renders the error boundary successfully
- The HTTP response is valid HTML
- The crash happens during client-side hydration

### Always Use Browser Verification

After ANY frontend fix, verify with actual browser rendering:

```bash
# Start dev-browser if not running
bash ~/.aidevops/agents/scripts/dev-browser-helper.sh start

# Take screenshot to verify
cd ~/.aidevops/dev-browser/skills/dev-browser && bun x tsx <<'EOF'
import { connect, waitForPageLoad } from "@/client.js";

const client = await connect("http://localhost:9222");
const page = await client.page("verify");

await page.goto("https://myapp.local");
await waitForPageLoad(page);

// Check for error indicators
const hasError = await page.evaluate(() => {
const body = document.body.innerText;
return body.includes("Something went wrong") ||
body.includes("Error:") ||
body.includes("Unhandled Runtime Error");
});
Comment on lines +75 to +80

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For more robust error detection, it's a good practice to make the search for error messages case-insensitive. This will prevent missing errors that might be capitalized differently (e.g., "error:" vs "Error:").

Suggested change
const hasError = await page.evaluate(() => {
const body = document.body.innerText;
return body.includes("Something went wrong") ||
body.includes("Error:") ||
body.includes("Unhandled Runtime Error");
});
const hasError = await page.evaluate(() => {
const body = document.body.innerText.toLowerCase();
return body.includes("something went wrong") ||
body.includes("error:") ||
body.includes("unhandled runtime error");
});


if (hasError) {
console.log("ERROR DETECTED - taking screenshot");
await page.screenshot({ path: "tmp/error-state.png", fullPage: true });
} else {
console.log("Page loaded successfully");
await page.screenshot({ path: "tmp/success-state.png" });
}

console.log({ url: page.url(), title: await page.title(), hasError });
await client.disconnect();
EOF
```

### When to Trigger Browser Verification

Automatically suggest browser verification when:

1. **After fixing any frontend error** - especially hydration/render errors
2. **User reports "not working"** but curl returns 200
3. **Modifying shared UI packages** in monorepos
4. **Changing component imports** or export patterns
5. **After clearing caches** (.next, node_modules)

## React Hydration Errors

### Understanding Hydration

Hydration = React attaching event handlers to server-rendered HTML. Fails when:
- Server HTML doesn't match client render
- Component returns invalid type (object instead of function)
- Browser APIs used during SSR

### Common Error Patterns

| Error Message | Likely Cause | Solution |
|---------------|--------------|----------|
| `Element type is invalid: expected string... got: object` | Import returning wrong type | Check import path, verify export is React component |
| `Hydration failed because initial UI does not match` | Server/client mismatch | Check for browser-only code, use `useEffect` for client-only |
| `Text content does not match` | Dynamic content in SSR | Use `suppressHydrationWarning` or move to client component |
| `Cannot read properties of undefined` | Missing data during SSR | Add null checks, use loading states |

### The "got: object" Pattern

This specific error almost always means an import is returning the wrong type:

```typescript
// BAD: SVGR import in shared package (returns object, not component)
import Logo from "./svg/logo.svg"; // Returns { src: "...", height: ..., width: ... }

// GOOD: Inline React component
export const Logo = (props: SVGProps<SVGSVGElement>) => (
<svg viewBox="0 0 100 100" {...props}>
<path d="..." />
</svg>
);
```

**Debugging steps**:
1. Find the component mentioned in error (e.g., `Header`)
2. Check all imports in that component
3. Look for non-standard imports (SVG, JSON, CSS modules)
4. Verify each import returns expected type

## Monorepo Package Boundaries

### The Webpack Loader Problem

Webpack loaders (SVGR, CSS modules, etc.) only process files within the app's webpack pipeline.

```text
apps/web/ # Webpack processes this
src/
components/
Logo.tsx # Can use: import Logo from "./logo.svg"

packages/ui/ # Transpiled by Next.js, NOT webpack
src/
icons.tsx # CANNOT use: import Logo from "./logo.svg"
# SVG import returns raw object, not component
```

### What Works vs What Doesn't

| Pattern | In App (`apps/web/`) | In Package (`packages/ui/`) |
|---------|---------------------|----------------------------|
| `import X from "./file.svg"` (SVGR) | Works | **Broken** - returns object |
| `import styles from "./file.module.css"` | Works | **Broken** - returns object |
| `import data from "./file.json"` | Works | Works (JSON is universal) |
| Inline React components | Works | Works |
| `@svgr/webpack` configured | Works | **Not applied** |

### Solutions for Shared Packages

**Option 1: Inline SVG Components (Recommended)**

```typescript
// packages/ui/shared/src/assets/icons.tsx

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with the file structure example provided earlier in the document (lines 157-161), the file path in this comment should probably be // packages/ui/src/assets/icons.tsx or simply // packages/ui/src/icons.tsx. The current path introduces a shared directory which wasn't mentioned before, potentially causing confusion.

Suggested change
// packages/ui/shared/src/assets/icons.tsx
// packages/ui/src/assets/icons.tsx

import type { SVGProps } from "react";

export const Logo = (props: SVGProps<SVGSVGElement>) => (
<svg viewBox="0 0 100 100" fill="currentColor" {...props}>
<path d="M10 10 L90 10 L90 90 L10 90 Z" />
</svg>
);

export const Icon = (props: SVGProps<SVGSVGElement>) => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" {...props}>
<path strokeLinecap="round" d="M12 4v16m-8-8h16" />
</svg>
);
```

**Option 2: Build-time SVG transformation**

Configure the shared package to transform SVGs during its own build:

```json
// packages/ui/package.json
{
"scripts": {
"build": "tsup --loader '.svg=dataurl'"
}
}
```

**Option 3: Re-export from app**

Keep SVG imports in the app, re-export to packages:

```typescript
// apps/web/src/assets/icons.ts
export { default as Logo } from "./svg/logo.svg";

// packages/ui uses the app's exports (requires careful dependency management)
```

### Detection Checklist

When working in `packages/` directories, check for:

- [ ] SVG imports (`import X from "*.svg"`)
- [ ] CSS module imports (`import styles from "*.module.css"`)
- [ ] Any webpack-loader-dependent imports
- [ ] Assets that work in `apps/` but might not in `packages/`

## Debugging Workflow

### Step 1: Reproduce with Browser

```bash
# Don't trust curl - use browser
bash ~/.aidevops/agents/scripts/dev-browser-helper.sh start
# Navigate to URL, take screenshot
```

### Step 2: Check Console Errors

```bash
cd ~/.aidevops/dev-browser/skills/dev-browser && bun x tsx <<'EOF'
import { connect, waitForPageLoad } from "@/client.js";

const client = await connect("http://localhost:9222");
const page = await client.page("debug");

// Capture console errors
const errors: string[] = [];
page.on('console', msg => {
if (msg.type() === 'error') errors.push(msg.text());
});
page.on('pageerror', err => errors.push(err.message));

await page.goto("https://myapp.local");
await waitForPageLoad(page);

console.log("Console errors:", errors);
await client.disconnect();
EOF
```

### Step 3: Identify Component

From error message, find the failing component:
- `Check the render method of 'Header'` → Look at Header component
- Trace imports back to source

### Step 4: Verify Import Types

```typescript
// Add temporary debug logging
console.log("Logo type:", typeof Logo, Logo);
// Object = broken import
// Function = valid React component
```

### Step 5: Fix and Verify with Browser

After fix, ALWAYS verify with browser screenshot, not curl.

## Related Resources

- **Browser automation**: `tools/browser/dev-browser.md`
- **React patterns**: `tools/ui/shadcn.md`
- **Build debugging**: `workflows/bug-fixing.md`
19 changes: 19 additions & 0 deletions .agent/workflows/bug-fixing.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,25 @@ composer test
bash ~/Git/aidevops/.agent/scripts/linters-local.sh
```

#### Frontend Bug Verification (CRITICAL)

**For React/Next.js/frontend bugs**: HTTP status codes do NOT verify the fix works.

```bash
# BAD: This returns 200 even when React crashes client-side
curl -s https://myapp.local -o /dev/null -w "%{http_code}"

# GOOD: Use browser screenshot to verify
bash ~/.aidevops/agents/scripts/dev-browser-helper.sh start
# Then navigate and screenshot - see tools/ui/frontend-debugging.md
```

**Why curl lies**: Server returns 200 because error boundaries render successfully. The crash happens during client-side hydration, which curl never executes.

**Always verify frontend fixes with actual browser rendering.**

See `tools/ui/frontend-debugging.md` for detailed browser verification workflow.

### 6. Commit Changes

Make atomic commits with clear messages:
Expand Down
Loading