diff --git a/.agents/skills/how-to-write-component/SKILL.md b/.agents/skills/how-to-write-component/SKILL.md index 8478a4824f..aa00c21ea7 100644 --- a/.agents/skills/how-to-write-component/SKILL.md +++ b/.agents/skills/how-to-write-component/SKILL.md @@ -13,18 +13,19 @@ Follow existing project patterns first. Use these rules to resolve unclear compo - Prefer `function` for top-level components and module helpers. Use arrow functions for local callbacks, handlers, and lambda-style APIs. - Prefer named exports. Use default exports only where the framework requires them, such as Next.js route files. -## Props And API Types +## Props, Naming, And API Types - Type simple one-off props inline. Use a named `Props` type only when reused, exported, complex, or clearer. - Use API-generated or API-returned types at component boundaries. Keep small UI conversion helpers beside the component that needs them. +- Name values by their domain role and keep that name stable across the call chain, especially IDs such as `appInstanceId`. Normalize framework or route params at the boundary instead of passing aliased names through components. - Keep fallback and invariant checks at the lowest component that already handles that state; callers should pass raw values through instead of duplicating checks. + +## State, Queries, And Callbacks + +- Keep local state, query state, handlers, mutations, and derived UI data at the lowest component that uses them. If no such component exists, extract a purpose-built component that owns that logic. - Avoid prop drilling. One pass-through layer is acceptable; two or more forwarding-only layers means ownership is in the wrong place. -- Put handlers, mutations, queries, and side effects at the component boundary that actually uses them. For row-level actions, the row or a purpose-built row container should own the action; list/layout components should pass data and stable IDs, not callbacks they do not use. +- For row-level actions, the row or a purpose-built row container should own the action; list/layout components should pass data and stable IDs, not callbacks they do not use. - Keep callbacks in a parent only when the parent genuinely coordinates the workflow, such as form submission, shared selection state, cross-row batch behavior, or navigation after a child action. - -## State Ownership - -- Keep local state, query state, handlers, and derived UI data near the interaction owner. Do not lift state unless siblings or parents genuinely coordinate through it. - For local UI state shared across siblings, distant children, or feature-local surfaces, use a colocated Jotai `atom`. Keep atoms feature-scoped and UI-owned; do not use them for server/cache state that belongs in query or API data flow. - Prefer uncontrolled components when DOM-owned state is enough. Expose style customization through CSS variables before adding controlled props only for visual changes. @@ -32,7 +33,7 @@ Follow existing project patterns first. Use these rules to resolve unclear compo - Separate first-render surfaces from secondary interactive surfaces. For dialogs, dropdowns, popovers, and similar branches, extract a small local component that owns the trigger, open state, and overlay/menu content when that branch obscures the parent flow. - Do not further split dialog bodies, menu bodies, or forms unless they have their own state, reuse, complexity, or semantic boundary. -- Avoid shallow wrappers and renaming. Call the original function directly unless the wrapper adds validation, orchestration, error handling, state ownership, or a real semantic boundary. +- Avoid shallow wrappers and prop renaming. Call the original function directly unless the wrapper adds validation, orchestration, error handling, state ownership, or a real semantic boundary. ## Navigation