Skip to content
Back to skills

Frontend Patterns

ASecurity

Frontend patterns — debounce, double-submit prevention, error boundaries.

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 5, 2026
developmentreactvueangularapifrontendbackend

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 5, 2026

npx -y skills add Dev-Toolbelt/dev-team-agents --skill frontend-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Frontend Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Frontend Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dev-toolbelt-frontend-patterns/badge)](https://www.skillsdirectory.com/skills/dev-toolbelt-frontend-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: frontend-patterns
description: Frontend patterns — debounce, double-submit prevention, error boundaries.
---

## Interaction Patterns

### Debounce

Apply debounce whenever user input triggers expensive side effects (API calls, heavy computation). Without it, every keystroke fires a request — degrading both UX and server load.

**When to use**: search/autocomplete inputs, address or tag lookup, form field validation against an API, resize/scroll event handlers.

**Framework-agnostic pattern** (adapt to the project's stack):
```ts
// Reusable debounce utility — use lodash.debounce, use-debounce, or VueUse's useDebounceFn
function debounce<T extends (...args: unknown[]) => void>(fn: T, delay: number): T {
  let timer: ReturnType<typeof setTimeout>
  return ((...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay) }) as T
}

// Autocomplete example
const searchUsers = debounce(async (query: string) => {
  if (query.length < 2) return
  results.value = await api.users.search(query)
}, 300)
```

**Rules**:
- 150–300 ms is the standard delay for search inputs; 500 ms+ for validation-only fields
- Cancel in-flight requests when a new debounced call fires (use `AbortController`)
- Clear the debounce timer on component teardown to prevent stale state updates

### Double Submission Prevention

Every form submit or action button must be guarded against duplicate submissions. Two clicks = two POST requests = corrupted data, duplicate payments, or duplicate records.

**Rules**:
- Track a boolean `isSubmitting` state; set it `true` on submission start, `false` on completion (success or error)
- Disable the submit button while `isSubmitting` is `true` — never just rely on UX
- The handler must bail early if already in flight:

```ts
// Framework-agnostic guard
async function handleSubmit() {
  if (isSubmitting) return        // ← guard: bail on double-click
  isSubmitting = true
  try {
    await api.createOrder(payload)
  } finally {
    isSubmitting = false           // ← always release, even on error
  }
}
```

- Use `finally` (or equivalent) — releasing the lock only on success is a common bug that permanently disables the form after an error
- For idempotency-critical operations (payments, order placement), also pass an idempotency key from the backend side

---

## Error Boundaries & Global Error Handling

A runtime error in one component must not crash the entire UI. Wrap every route/page-level component in an error boundary that shows a user-facing fallback.

- **React**: class component with `componentDidCatch`, or `react-error-boundary` (`<ErrorBoundary FallbackComponent={...}>`)
- **Vue**: `onErrorCaptured` in a wrapper component, or `app.config.errorHandler` for global handling
- **Angular**: implement `ErrorHandler` and provide it at the root level (`{ provide: ErrorHandler, useClass: AppErrorHandler }`)
- **Svelte**: `<svelte:boundary>` (Svelte 5) or a wrapper component with `onerror`

**Rules:**
- Place boundaries at the **route/page level** at minimum — a broken widget must not take down the entire app
- Always show a user-facing fallback — never a blank screen or a raw JS stack trace
- Register a global `window.addEventListener('unhandledrejection', ...)` to catch uncaught promise rejections and forward them to the project's error tracking service (Sentry, Datadog, etc.)
- **Never swallow errors silently** — catching without logging or reporting creates invisible bugs

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.