Files
asepharyana-hub-guide/skills/react-frontend/SKILL.md
T
asepharyana e513cddd68 feat(hub-guide): expand plugin with 26 best-practice skills, hooks, and references
Transform hub-guide from a single-skill Hub monorepo guide into a
comprehensive programming best-practice plugin covering all situations.

Skills (26):
- Core: engineering-principles, clean-code, clean-architecture,
  design-patterns, testing, error-handling, security, api-design,
  git-workflow, documentation, logging-observability, performance
- Languages: typescript, python, rust, go
- Frameworks: react-frontend, elysiajs, hono-backend, drizzle-database, nextjs
- Infrastructure: docker, ci-cd, monitoring
- Monorepo: monorepo, hub-guide (existing)

Hooks:
- SessionStart: auto-detect project type and activate relevant skills
- PreToolUse (Write|Edit): inject language-specific rules per file type

Reference files for deep dives:
- clean-architecture/references/solid.md (SOLID + component principles)
- design-patterns/references/catalog.md (full GoF catalog with examples)
- testing/references/mocks.md (test double taxonomy)

Restructure plugin to modern skills/ directory format.
2026-07-25 11:35:16 +07:00

6.6 KiB

name, description
name description
react-frontend React and frontend best practices — component patterns, hooks, state management, TanStack Query, React Router, performance, and testing. Use when building React components, designing state management, or whenever the user mentions "React," "hooks," "state management," "component," "JSX," "TanStack Query," "React Router," "Zustand," "Vite," "Next.js," or "Frontend."

React Frontend Best Practices

Component Patterns

Composition over Inheritance

// ✅ Prefer composition
function Layout({ sidebar, children }: { sidebar: ReactNode; children: ReactNode }) {
  return <div className="layout">{sidebar}<main>{children}</main></div>;
}

// ❌ Avoid inheritance patterns in React

Container/Presentational Separation

// Container: manages state, data fetching, business logic
function UserProfileContainer() {
  const { data: user } = useUserQuery(userId);
  const { mutate: update } = useUpdateUserMutation();
  return <UserProfile user={user!} onUpdate={update} />;
}

// Presentational: pure rendering, props-only
function UserProfile({ user, onUpdate }: UserProfileProps) {
  return <div>{user.name} <button onClick={onUpdate}>Edit</button></div>;
}

Custom Hooks for Logic Extraction

// Extract reusable logic into custom hooks
function useUserPermissions(userId: string) {
  const { data: user } = useUserQuery(userId);
  return useMemo(() => ({
    isAdmin: user?.role === 'admin',
    canEdit: user?.role === 'admin' || user?.role === 'editor',
    canDelete: user?.role === 'admin',
  }), [user]);
}

Hooks Rules

  • Only call hooks at the top level — not in conditions, loops, or callbacks.
  • Only call hooks from React functions — component or custom hook.
  • Deps array matches reality — include all values used inside.
  • useMemo for expensive computations. useCallback for stable references.
  • useEffect is for synchronization, not lifecycle. If you can compute from state, do it.
// ❌ Unnecessary effect
const [fullName, setFullName] = useState('');
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]);

// ✅ Derived state
const fullName = `${first} ${last}`;

Data Fetching (TanStack Query)

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// Query
function useUserQuery(id: string) {
  return useQuery({
    queryKey: ['users', id],
    queryFn: () => api.users.get({ params: { id } }),
    staleTime: 30_000,  // 30s before refetch
    gcTime: 5 * 60_000, // 5min cache
  });
}

// Mutation with optimistic update
function useUpdateUserMutation() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (data: UpdateUserInput) => api.users.update({ body: data }),
    onMutate: async (newUser) => {
      await queryClient.cancelQueries({ queryKey: ['users', newUser.id] });
      const previous = queryClient.getQueryData(['users', newUser.id]);
      queryClient.setQueryData(['users', newUser.id], newUser);
      return { previous };
    },
    onError: (_, __, context) => {
      queryClient.setQueryData(['users', context.previous.id], context.previous);
    },
    onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
  });
}

Rules:

  • staleTime for read-through caching. Default 0 = refetch on mount.
  • gcTime for garbage collection of unused data.
  • onMutate/onError/onSettled for optimistic updates.
  • Queries over custom fetch + useEffect in all cases.

State Management Selection

Need Solution
Server state TanStack Query
URL state React Router / TanStack Router
Form state React Hook Form + Zod
Client state (global) Zustand or Context
Client state (local) useState / useReducer
Component communication Props / lifting state up
// Zustand — lightweight, no boilerplate
import { create } from 'zustand';

interface UIStore {
  sidebarOpen: boolean;
  toggleSidebar: () => void;
}
const useUIStore = create<UIStore>((set) => ({
  sidebarOpen: true,
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}));

Routing (TanStack Router / React Router)

// TanStack Router — type-safe, modern
const router = createRouter({
  routeTree: rootRoute.addChildren([
    indexRoute,
    usersRoute.addChildren([userRoute, userProfileRoute]),
  ]),
});
  • File-based routing (Next.js App Router, Vite/Router) for simpler projects.
  • Type-safe routers (TanStack Router) for larger apps.
  • Lazy load route components — React.lazy(() => import('./routes/Dashboard')).

Performance

  • Virtual lists@tanstack/react-virtual for 100+ items.
  • React.memo sparingly — only for components that re-render often with same props.
  • useMemo for expensive calculations — not for every value.
  • Code splitting — per route, per heavy component.
  • Bundle analysisvite-bundle-visualizer to find bloat.

Testing (Vitest + Testing Library)

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserProfile } from './UserProfile';

describe('UserProfile', () => {
  it('renders user name', () => {
    render(<UserProfile user={{ name: 'Alice' }} />);
    expect(screen.getByText('Alice')).toBeInTheDocument();
  });

  it('calls onUpdate when edit clicked', async () => {
    const onUpdate = vi.fn();
    render(<UserProfile user={{ name: 'Alice' }} onUpdate={onUpdate} />);
    await userEvent.click(screen.getByRole('button', { name: /edit/i }));
    expect(onUpdate).toHaveBeenCalledTimes(1);
  });
});
  • Testing Library for user-centric tests. Never test implementation details.
  • userEvent over fireEvent — simulates real user interactions.
  • Component-level tests for behavior, not storybook-style visual tests here.

CSS / Styling

  • Tailwind CSS for utility-first styling. Consistent, fast, small.
  • CSS Modules when you need scoped component styles.
  • CSS-in-JS (styled-components, emotion) — only for dynamic theming. Prefer Tailwind.

Anti-patterns

  • useEffect for data fetching — use TanStack Query
  • Prop drilling beyond 3 levels — compose or context
  • useState for derived data — compute from existing state
  • Direct DOM manipulation — use React refs
  • any in component props — always type props
  • Large component files — split by responsibility
  • index as key — breaks reconciliation on reorder
  • useEffect without deps — runs every render