performance-security

Use when optimizing performance or reviewing security. Covers code-splitting, React Compiler patterns, asset optimization, a11y testing, and security hardening for React apps.

248 stars

Best use case

performance-security is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when optimizing performance or reviewing security. Covers code-splitting, React Compiler patterns, asset optimization, a11y testing, and security hardening for React apps.

Teams using performance-security should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/performance-security/SKILL.md --create-dirs "https://raw.githubusercontent.com/MadAppGang/claude-code/main/plugins/frontend/skills/performance-security/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/performance-security/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How performance-security Compares

Feature / Agentperformance-securityStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when optimizing performance or reviewing security. Covers code-splitting, React Compiler patterns, asset optimization, a11y testing, and security hardening for React apps.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Performance, Accessibility & Security

Production-ready patterns for building fast, accessible, and secure React applications.

## Performance Optimization

### Code-Splitting

**Automatic with TanStack Router:**
- File-based routing automatically code-splits by route
- Each route is its own chunk
- Vite handles dynamic imports efficiently

**Manual code-splitting:**
```typescript
import { lazy, Suspense } from 'react'

// Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'))

function Dashboard() {
  return (
    <Suspense fallback={<Spinner />}>
      <HeavyChart data={data} />
    </Suspense>
  )
}
```

**Route-level lazy loading:**
```typescript
// src/routes/dashboard.lazy.tsx
export const Route = createLazyFileRoute('/dashboard')({
  component: DashboardComponent,
})
```

### React Compiler First

The React Compiler automatically optimizes performance when you write compiler-friendly code:

**✅ Do:**
- Keep components pure (no side effects in render)
- Derive values during render (don't stash in refs)
- Keep props serializable
- Inline event handlers (unless they close over large objects)

**❌ Avoid:**
- Mutating props or state
- Side effects in render phase
- Over-using useCallback/useMemo (compiler handles this)
- Non-serializable props (functions, symbols)

**Verify optimization:**
- Check React DevTools for "Memo ✨" badge
- Components without badge weren't optimized (check for violations)

### Images & Assets

**Use Vite asset pipeline:**
```typescript
// Imports are optimized and hashed
import logo from './logo.png'

<img src={logo} alt="Logo" />
```

**Prefer modern formats:**
```typescript
// WebP for photos
<img src="/hero.webp" alt="Hero" />

// SVG for icons
import { ReactComponent as Icon } from './icon.svg'
<Icon />
```

**Lazy load images:**
```typescript
<img src={imageSrc} loading="lazy" alt="Description" />
```

**Responsive images:**
```typescript
<img
  srcSet="
    /image-320w.webp 320w,
    /image-640w.webp 640w,
    /image-1280w.webp 1280w
  "
  sizes="(max-width: 640px) 100vw, 640px"
  src="/image-640w.webp"
  alt="Description"
/>
```

### Bundle Analysis

```bash
# Build with analysis
npx vite build --mode production

# Visualize bundle
pnpm add -D rollup-plugin-visualizer
```

```typescript
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    react(),
    visualizer({ open: true }),
  ],
})
```

### Performance Checklist

- [ ] Code-split routes and heavy components
- [ ] Verify React Compiler optimizations (✨ badges)
- [ ] Optimize images (WebP, lazy loading, responsive)
- [ ] Prefetch critical data in route loaders
- [ ] Use TanStack Query for automatic deduplication
- [ ] Set appropriate `staleTime` per query
- [ ] Minimize bundle size (check with visualizer)
- [ ] Enable compression (gzip/brotli on server)

## Accessibility (a11y)

### Semantic HTML

**✅ Use semantic elements:**
```typescript
// Good
<nav><a href="/about">About</a></nav>
<button onClick={handleClick}>Submit</button>
<main><article>Content</article></main>

// Bad
<div onClick={handleNav}>About</div>
<div onClick={handleClick}>Submit</div>
<div><div>Content</div></div>
```

### ARIA When Needed

**Only add ARIA when semantic HTML isn't enough:**
```typescript
// Custom select component
<div
  role="listbox"
  aria-label="Select country"
  aria-activedescendant={activeId}
>
  <div role="option" id="us">United States</div>
  <div role="option" id="uk">United Kingdom</div>
</div>

// Loading state
<button aria-busy={isLoading} disabled={isLoading}>
  {isLoading ? 'Loading...' : 'Submit'}
</button>
```

### Keyboard Navigation

**Ensure all interactive elements are keyboard accessible:**
```typescript
function Dialog({ isOpen, onClose }: DialogProps) {
  useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose()
    }

    if (isOpen) {
      document.addEventListener('keydown', handleEscape)
      return () => document.removeEventListener('keydown', handleEscape)
    }
  }, [isOpen, onClose])

  return isOpen ? (
    <div role="dialog" aria-modal="true">
      {/* Focus trap implementation */}
      <button onClick={onClose} aria-label="Close dialog">×</button>
      {/* Dialog content */}
    </div>
  ) : null
}
```

### Testing with React Testing Library

**Use accessible queries (by role/label):**
```typescript
import { render, screen } from '@testing-library/react'

test('button is accessible', () => {
  render(<button>Submit</button>)

  // ✅ Good - query by role
  const button = screen.getByRole('button', { name: /submit/i })
  expect(button).toBeInTheDocument()

  // ❌ Avoid - query by test ID
  const button = screen.getByTestId('submit-button')
})
```

**Common accessible queries:**
```typescript
// By role (preferred)
screen.getByRole('button', { name: /submit/i })
screen.getByRole('textbox', { name: /email/i })
screen.getByRole('heading', { level: 1 })

// By label
screen.getByLabelText(/email address/i)

// By text
screen.getByText(/welcome/i)
```

### Color Contrast

- Ensure 4.5:1 contrast ratio for normal text
- Ensure 3:1 contrast ratio for large text (18pt+)
- Don't rely on color alone for meaning
- Test with browser DevTools accessibility panel

### Accessibility Checklist

- [ ] Use semantic HTML elements
- [ ] Add alt text to all images
- [ ] Ensure keyboard navigation works
- [ ] Provide focus indicators
- [ ] Test with screen reader (NVDA/JAWS/VoiceOver)
- [ ] Verify color contrast meets WCAG AA
- [ ] Use React Testing Library accessible queries
- [ ] Add skip links for main content
- [ ] Ensure form inputs have labels

## Security

### Never Ship Secrets

**❌ Wrong - secrets in code:**
```typescript
const API_KEY = 'sk_live_abc123' // Exposed in bundle!
```

**✅ Correct - environment variables:**
```typescript
// Only VITE_* variables are exposed to client
const API_KEY = import.meta.env.VITE_PUBLIC_KEY
```

**In `.env.local` (not committed):**
```bash
VITE_PUBLIC_KEY=pk_live_abc123  # Public key only!
```

**Backend handles secrets:**
```typescript
// Frontend calls backend, backend uses secret API key
await apiClient.post('/process-payment', { amount, token })
// Backend has access to SECRET_KEY via server env
```

### Validate All Untrusted Data

**At boundaries (API responses):**
```typescript
import { z } from 'zod'

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
})

async function fetchUser(id: string) {
  const response = await apiClient.get(`/users/${id}`)

  // Validate response
  return UserSchema.parse(response.data)
}
```

**User input:**
```typescript
const formSchema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Password must be 8+ characters'),
})

type FormData = z.infer<typeof formSchema>

function LoginForm() {
  const handleSubmit = (data: unknown) => {
    const result = formSchema.safeParse(data)

    if (!result.success) {
      setErrors(result.error.errors)
      return
    }

    // result.data is typed and validated
    login(result.data)
  }
}
```

### XSS Prevention

React automatically escapes content in JSX:
```typescript
// ✅ Safe - React escapes
<div>{userInput}</div>

// ❌ Dangerous - bypasses escaping
<div dangerouslySetInnerHTML={{ __html: userInput }} />
```

**If you must use HTML:**
```typescript
import DOMPurify from 'dompurify'

<div dangerouslySetInnerHTML={{
  __html: DOMPurify.sanitize(trustedHTML)
}} />
```

### Content Security Policy

Add CSP headers on server:
```nginx
# nginx example
add_header Content-Security-Policy "
  default-src 'self';
  script-src 'self' 'unsafe-inline';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self' data:;
  connect-src 'self' https://api.example.com;
";
```

### Dependency Security

**Pin versions in package.json:**
```json
{
  "dependencies": {
    "react": "19.0.0",  // Exact version
    "@tanstack/react-query": "^5.59.0"  // Allow patches
  }
}
```

**Audit regularly:**
```bash
pnpm audit
pnpm audit --fix
```

**Use Renovate or Dependabot:**
```json
// .github/renovate.json
{
  "extends": ["config:base"],
  "automerge": true,
  "major": { "automerge": false }
}
```

### CI Security

**Run with `--ignore-scripts`:**
```bash
# Prevents malicious post-install scripts
pnpm install --ignore-scripts
```

**Scan for secrets:**
```bash
# Add to CI
git-secrets --scan
```

### Security Checklist

- [ ] Never commit secrets or API keys
- [ ] Only expose `VITE_*` env vars to client
- [ ] Validate all API responses with Zod
- [ ] Sanitize user-generated HTML (if needed)
- [ ] Set Content Security Policy headers
- [ ] Pin dependency versions
- [ ] Run `pnpm audit` regularly
- [ ] Enable Renovate/Dependabot
- [ ] Use `--ignore-scripts` in CI
- [ ] Implement proper authentication flow

## Related Skills

- **core-principles** - Project structure and standards
- **react-patterns** - Compiler-friendly code
- **tanstack-query** - Performance via caching and deduplication
- **tooling-setup** - TypeScript strict mode for type safety

Related Skills

performance-correlation

248
from MadAppGang/claude-code

Correlate content attributes with GA4 and GSC metrics to identify performance drivers

performance-tracking

248
from MadAppGang/claude-code

Track agent, skill, and model performance metrics for optimization. Use when measuring agent success rates, tracking model latency, analyzing routing effectiveness, or optimizing cost-per-task. Trigger keywords - "performance", "metrics", "tracking", "success rate", "agent performance", "model latency", "cost tracking", "optimization", "routing metrics".

golang-performance

248
from MadAppGang/claude-code

Use when profiling Go applications (pprof), running benchmarks, optimizing memory/CPU usage, or debugging performance bottlenecks in production Go code.

test-skill

248
from MadAppGang/claude-code

A test skill for validation testing. Use when testing skill parsing and validation logic.

bad-skill

248
from MadAppGang/claude-code

This skill has invalid YAML in frontmatter

release

248
from MadAppGang/claude-code

Plugin release process for MAG Claude Plugins marketplace. Covers version bumping, marketplace.json updates, git tagging, and common mistakes. Use when releasing new plugin versions or troubleshooting update issues.

openrouter-trending-models

248
from MadAppGang/claude-code

Fetch trending programming models from OpenRouter rankings. Use when selecting models for multi-model review, updating model recommendations, or researching current AI coding trends. Provides model IDs, context windows, pricing, and usage statistics from the most recent week.

Claudish Integration Skill

248
from MadAppGang/claude-code

**Version:** 1.0.0

transcription

248
from MadAppGang/claude-code

Audio/video transcription using OpenAI Whisper. Covers installation, model selection, transcript formats (SRT, VTT, JSON), timing synchronization, and speaker diarization. Use when transcribing media or generating subtitles.

final-cut-pro

248
from MadAppGang/claude-code

Apple Final Cut Pro FCPXML format reference. Covers project structure, timeline creation, clip references, effects, and transitions. Use when generating FCP projects or understanding FCPXML structure.

ffmpeg-core

248
from MadAppGang/claude-code

FFmpeg fundamentals for video/audio manipulation. Covers common operations (trim, concat, convert, extract), codec selection, filter chains, and performance optimization. Use when planning or executing video processing tasks.

statusline-customization

248
from MadAppGang/claude-code

Configuration reference and troubleshooting for the statusline plugin — sections, themes, bar widths, and script architecture