Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

narrow-react-prop-types — Claude Code skill: install & use

A skill that cuts React component prop types back to the contract your live code actually uses, then makes stories and tests match it instead of weakening it.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add humanlayer/skills --skill narrow-react-prop-types -a claude-code
Open on GitHub

What narrow-react-prop-types does

The skill takes a React component whose prop types were widened for stories, mocks or demos and shrinks them to what the running app really passes. It flags suspects first: interfaces full of optional fields, calls like onSelect?.(...), fallbacks like items ?? []. Then it walks every import and splits call sites into live paths and support code such as stories, tests and fixtures. Only live paths get a say in the component API.

Optional and nullable are not the same thing. If live code always passes a prop but the value can be empty, the skill suggests keeping it required with | null. Types get derived where possible: Parameters<typeof fn>[0], ReturnType<typeof fn>, Extract<Union, Shape>, and state setters written as React.Dispatch<React.SetStateAction<T>>. The same pass tightens child primitives and deletes guards for states that never happen.

Validation runs per package and per consumer; in the author's monorepo that means bun --bun run typecheck --filter <package>. Expect stories and tests to break. Fix them with realistic handlers rather than putting the ? back. A response template in references/response-template.md formats the CI run, and that text becomes the PR body.

What it helps with

  • drop optional callbacks from menu items that always render
  • derive a prop type from an existing function instead of retyping it
  • turn focusedItem?: T into a required focusedItem: T | null
  • strip fallback logic left over from wide prop types
  • repair broken stories with a shared fixture helper

Example prompts for the agent

  • Narrow the `UserRow` props to the live call sites and remove the optional handlers.
  • Check whether live code ever calls `onArchive` in `ArchiveMenu`, and make the prop required if it does.
  • Run typecheck on the package and its consumers once the `Dialog` props are narrowed.

How to install narrow-react-prop-types

1Skills CLI (Vercel)

npx skills add humanlayer/skills --skill narrow-react-prop-types -a claude-code

2Manually: put the files in place

~/.claude/skills/narrow-react-prop-types/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/narrow-react-prop-types/

From the same set6

The whole set →

Similar

migrate-nativewind-to-uniwind uni-stack A skill that automates migrating React Native projects from NativeWind to Uniwind. It updates configs, removes old packages, rebuilds CSS, and handles all breaking changes. Claude Code CX Codex Cursor +1 2,000installs react-router TanStack A skill that teaches your coding agent the React bindings for TanStack Router: hook reference, components, and a file-based routing setup on Vite. Claude Code CX Codex Cursor +1 546installs wpds WordPress Feeds WordPress Design System docs to your agent through an MCP server, so UI work lands on real components and tokens instead of guesses. Claude Code CX Codex Cursor +1 4,000installs tanstack-query-best-practices DeckardGer A rule set for TanStack Query in React: query keys, caching, mutations, SSR. Installs into Claude Code, Codex, Cursor or Copilot so data fetching follows one pattern across the repo. Claude Code CX Codex Cursor +1 12,800installs senior-frontend Alireza Rezvani This skill supports React and Next.js work, from project scaffolding to bundle checks. Use it when building components, reviewing frontend code, or working on accessibility. Claude Code 1,630installs generate Alireza Rezvani The generate skill writes Playwright tests from a user story, URL, component name or feature description. Useful when an e2e scenario needs covering and you would rather not hand-write it. Claude Code 1,624installs frontend-design official anthropics This skill guides intentional visual design for new interfaces and redesigns. Use it when a generic generated UI is not enough. Claude Code Claude.ai 1,025,790installs design-taste-frontend Leonxlnx This skill directs the design of public pages and work screens, then checks the result in a browser through screenshots. Use it when an interface feels generic or unfinished. Claude Code CX Codex Cursor +1 673,100installs

Questions about narrow-react-prop-types

How do I install narrow-react-prop-types in Claude Code?

Run npx skills add humanlayer/skills --skill narrow-react-prop-types -a claude-code. The skill lands at ~/.claude/skills/narrow-react-prop-types/SKILL.md.

What does narrow-react-prop-types do?

It tightens the exported prop type to states seen in live code and removes the defensive render logic that only existed because the type was loose.

Do the stories and tests survive the change?

Often not. You update the support code with realistic handlers and fixtures; the skill explicitly keeps you from re-adding optional props just to cut test setup.

Source text

SKILL.md · MIT
---
name: narrow-react-prop-types
description: narrow React component prop types to match live code paths
---

# Narrow React Prop Types

Use this skill when a React component's props have been widened for stories, mocks, tests, or demos and now express states the live application does not enter.

The goal is to make component types describe the real live-code-path contract, then require stories/tests/mocks to adapt to that contract instead of weakening it.

## Core Requirements

- Find the actual non-test, non-Storybook call sites before changing types.
- Treat live code paths as the source of truth for the prop contract.
- Do not preserve optional props only because they make Storybook, tests, or mock data easier.
- Keep props optional only when there are non-Storybook, non-test call sites which do not provide them and which have a good reason for not doing so.
- Types should not enable expressing states which are not observed in non-test, non-Storybook call sites.
- Types should be as strict as possible so code can be as simple as possible.
- Prefer deriving and extracting types from existing live-code-path values and APIs where possible.

## Workflow

For an example recurring GitHub Actions workflow that runs this skill through CodeLayer, see `references/agent-narrow-component-props.yml`. Its example agent memory file is `references/narrow-component-props-memory.md`. For CI agent response formatting, see `references/response-template.md`.

### 1. Identify the suspect component

Look for components with these signals:

- Large props interfaces with many optional fields.
- Optional callback calls such as `onSelect?.(...)` or `onArchive?.(...)`.
- Fallback state handling such as `items ?? []`, `count ?? 0`, or `handler && ...` around values live code likely always supplies.
- UI affordances that always render even though their callbacks are optional.
- Props that look demo-oriented, such as `defaultFoo`, alternate handler shapes, or display toggles not used by live code.

Do not pick a target from a story or test alone. Use stories/tests only as supporting evidence that the type has been widened, not as evidence that a state is real.

### 2. Find every live usage

Search for all imports/usages of the component, exported prop type, and shared child primitives.

Classify call sites by whether they are live code paths or support code:

- Live code paths: app routes, wired components, providers, hooks, production package exports, and shared components used by those paths.
- Support code: Storybook stories, test files, fixtures, mocks, demo harnesses, and visual-only examples.

Only live code paths should determine what the component API supports.

### 3. Derive the real types from the live code paths

Read the live call sites and classify each prop:

- Required: every non-test, non-Storybook call site supplies it.
- Optional: at least one non-test, non-Storybook call site omits it and that omission is a meaningful runtime state.
- Removed: no non-test, non-Storybook call site uses it.

Nullability and optionality are different. If live code always passes a prop but the value can be empty, prefer a required nullable prop such as `focusedItem: FocusedItem | null` over `focusedItem?: FocusedItem | null`.

### 4. Tighten the public prop type

Update exported prop types to match only the states observed in live code paths.

The looser and more optional a type is, the more possible states the component has to reason about. Every optional prop creates another branch the component must handle, test, and keep correct. Prefer strict types that prevent impossible states instead of broad types that require defensive render logic.

If the component always renders an interactive affordance, require the handler that makes it work. Do not allow inert states like a visible menu item that calls `onRename?.(...)`.

### 5. Derive and extract types where possible

Prefer deriving types from the live APIs instead of restating them manually:

- `Parameters<typeof fn>[0]` for function argument types.
- `ReturnType<typeof fn>` for return types.
- `Extract<Union, Shape>` for narrowing a union to a real variant.
- `React.Dispatch<React.SetStateAction<T>>` for React state setters instead of approximating them as `(value: T) => void`.

Prefer explicit state type parameters when inference would widen or obscure the intended state shape:

```ts
const [dialogState, setDialogState] = useState<DialogState>({
  id: null,
  isOpen: false,
})
```

Avoid relying on implicit `useState(...)` inference when it produces broad nullable object shapes, string literal widening, or callback types that later need hand-written approximations.

### 6. Tighten internal child props too

Do not stop at the exported component if it passes broad props into child primitives.

If row/menu/button child components receive optional handlers only because the parent props were broad, tighten those internal props too. Replace optional calls like this:

```ts
onRename?.(id, name)
```

with required calls:

```ts
onRename(id, name)
```

### 7. Remove fallback logic for unsupported states

Once props are required, remove defensive fallbacks that only existed for widened types.

Examples:

```ts
new Set(expandedIds ?? defaultExpandedIds ?? [])
```

should become:

```ts
new Set(expandedIds)
```

```ts
items && items.length > 0
```

should become:

```ts
items.length > 0
```

### 8. Update all variants that share the prop type

If multiple components share the broad prop type, update them together so they all enforce the same live-code-path contract.

### 9. Let tests and stories adapt to live code

If a story or test breaks after narrowing props, fix it by providing realistic handlers and state. Do not make live-code-path props optional again to reduce test setup.

If the story/test setup feels verbose, create a test helper or fixture that satisfies the strict live-code-path contract. Keep the helper in support code; do not weaken the component API.

### 10. Validate the change

Run package-level typechecks for the changed package and each live app/package that consumes the changed component.

Use repository-specific validation commands when available. In this monorepo, prefer:

```bash
bun --bun run typecheck --filter <package>
```

### 11. Format the response

When running as a CI agent, format your final response according to `references/response-template.md`. This response becomes the PR body.

Include:
- Summary of how many components were narrowed
- Table of changes with rationale
- Live call sites that justify each narrowing
- Support code (stories/tests) that needed updating
- Validation results
- Risk assessment

## Review Checklist

- The changed prop type was derived from non-test, non-Storybook call sites.
- Optional callbacks are removed for always-rendered interactions.
- Rendered menu items and buttons cannot be inert because of missing handlers.
- Removed props are not used by live code paths.
- Nullability is preserved only for real states, such as no current focus.
- Types are derived or extracted where possible rather than manually duplicated.
- `useState<T>(...)` is used where inference would otherwise widen or obscure the intended state.
- Shared variants compile against the same narrowed contract.
- Typecheck passes for the shared package and consuming live app/package.

## Anti-Patterns to Avoid

- Making callbacks optional so stories can omit them.
- Rendering a menu item that calls `onAction?.(...)`.
- Adding `default*` props for Storybook when live code is controlled.
- Using `?? []` or `?? 0` to hide missing required live state.
- Accepting multiple API shapes when live code only uses one.
- Treating pure components as mock components with relaxed contracts.

© humanlayer · MIT · humanlayer/skills

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor