원클릭으로
frontend-philosophy
React/Preact frontend conventions: TanStack Query for server state, 8-section components, no TypeScript, no barrel files.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
React/Preact frontend conventions: TanStack Query for server state, 8-section components, no TypeScript, no barrel files.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Run the announcement round for a finished project: attribution check, directory submissions, launch-post drafts in Mike's voice. Drafts only — never posts. Trigger: /announce <project>, 'announce', 'launch round', 'tell people about'.
Novel ignition ritual for story-bible: 3-line 'previously on' + one pulling question. Body-double for getting into writing state, zero obligation. Trigger: /ignite, 'ignite', 'novel time', 'story time'.
Read/post Twitter/X via `bird` CLI: tweets, threads, search, timelines, bookmarks, follows, lists, media.
Drive VISIBLE Chrome with real profile/logins (CDP :9222). Screenshots, JS eval, interactive element picking, cookies. Frontend testing, authed flows, anything user should see. Headless text-only reading → Browser* tools. Linux + macOS.
Read before git commits.
Compact current conversation into handoff doc for another agent.
| name | frontend-philosophy |
| description | React/Preact frontend conventions: TanStack Query for server state, 8-section components, no TypeScript, no barrel files. |
Authoritative conventions for all React/Preact work. These are not suggestions — they are the standard. When in doubt, choose the simpler path.
"Every line of code is a liability. The best code is no code. The second best is simple, boring code that obviously works."
| Purpose | Default | Notes |
|---|---|---|
| Runtime/pkg | Bun | Fallback: npm. Never pnpm/yarn |
| Bundler | Vite | With @/ path alias |
| Framework | React or Preact | + TanStack Query/Router |
| Styling | Tailwind CSS | Utility-first, cn() for merging |
| UI primitives | shadcn/ui pattern | CVA for variants, Radix primitives |
| Server state | TanStack Query | ONLY source of truth for API data |
| Client state | Zustand or Preact Signals | UI state only (modals, theme, filters) |
| Icons | @remixicon/react | Ri*Line for outline, Ri*Fill for solid |
| Types | No TypeScript | .jsx components, .js utilities |
Speed over ceremony. Type gymnastics add grief, not joy. Runtime validation at system boundaries, shared constants, JSDoc for complex functions.
src/
├── components/
│ ├── ui/ # Base UI (shadcn pattern)
│ ├── billing/ # Feature-based grouping
│ ├── support/ # NOT type-based (lists/, forms/, displays/)
│ └── common/ # Shared across features
├── hooks/ # Custom hooks (use* prefix)
├── stores/ # Zustand stores
├── constants/ # Shared constants
├── lib/ # Utilities (cn, formatters)
├── pages/ # Route-level components
└── api/ # API client
Naming rules:
.jsx components, .js utilities — alwayshandle* event handlers, use* hooks, render* render methodsEvery component follows this order. See references/component-patterns.md for full examples.
const Component = ({ prop1, prop2 }) => {
// 1. Server state (TanStack Query)
const { data, isLoading, error } = useQuery({ ... });
// 2. Global client state (Zustand/signals)
const { theme } = useThemeStore();
// 3. Local state (useState)
const [isOpen, setIsOpen] = useState(false);
// 4. Refs
const formRef = useRef(null);
// 5. Custom hooks
const { isOnline } = useNetworkStatus();
// 6. Derived values (compute in render, no useState)
const isValid = email.includes("@");
const fullName = `${data?.first} ${data?.last}`;
// 7. Effects (DOM sync, subscriptions, analytics ONLY)
useEffect(() => { ... }, [deps]);
// 8. Event handlers
const handleSubmit = () => { ... };
// Return — early returns for loading/error first
if (isLoading) return <Skeleton />;
if (error) return <ErrorMessage error={error} />;
return ( ... );
};
// CORRECT — single source of truth
const { data: users } = useQuery({
queryKey: ["users", filters],
queryFn: () => api.get("/users", { params: filters }),
staleTime: 5 * 60 * 1000,
gcTime: 60 * 60 * 1000, // NOT cacheTime (v5)
});
// WRONG — duplicating server data in local state
const [users, setUsers] = useState([]);
useEffect(() => { fetchUsers().then(setUsers); }, []);
Only for UI concerns: modals, theme, sidebar, filters, form inputs.
// CORRECT — derive in render
const hasError = error !== null;
const activeUsers = users?.filter(u => u.isActive);
// WRONG — useState for derived values
const [hasError, setHasError] = useState(false);
useEffect(() => setHasError(error !== null), [error]);
These are hard rules, not guidelines.
| NEVER do this | Do this instead |
|---|---|
useEffect for state sync | Move logic to event handlers |
useCallback | Just define the function (almost never needed) |
useState for derived values | Compute in render |
| Duplicate server data in useState | TanStack Query is the source of truth |
| Barrel files (index.ts re-exports) | Import directly from the file |
useMemo without profiling first | Just compute it |
| Prop drilling beyond 2 levels | Composition or Zustand |
| Business logic in components | Extract to hooks or utilities |
| Multiple state management libs | TanStack Query + ONE client state lib |
| Inline styles or global CSS | Tailwind utilities |
| Premature abstraction | Rule of 3 — wait for 3+ use cases |
| Custom wrappers around libraries | Use the library directly |
| Over-commenting | Self-documenting names |
Only three:
Everything else belongs in an event handler or TanStack Query.
Prettier defaults:
rounded-fullrounded-xlcn() from @/lib/utils for class mergingdata-slot attributes for CSS targetingBefore shipping, ask: