agentsclimarketplace

React

Skill endorphin-ai/claude-code-teams/go-react-team/.claude/skills/react

πŸ• One Pizza Team [AI Agents Team ] | Claude Code Agent Squads

Install
npx -y skills add endorphin-ai/claude-code-teams --skill react

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

React frontend development skill with TypeScript, Vite, TanStack Query, and Tailwind CSS. Use when implementing React UI components and pages.

SKILL.md

17.5 KB, as published. Nobody here has run it

React Frontend Development

Purpose

This skill provides the complete knowledge base for building React frontends in a fullstack Golang + React application. It covers component architecture, state management, API integration, routing, styling, and form handling. The react-dev agent uses this skill to implement UI features that consume the Go backend API.

Technology Stack

TechnologyVersionPurpose
React18+UI library (functional components only)
TypeScript5.x (strict mode)Type safety
Vite5.xBuild tool and dev server
TanStack Queryv5Server state management
React Routerv6Client-side routing with lazy loading
Tailwind CSS3.xUtility-first styling
React Hook Form7.xForm state management
zod3.xSchema validation (forms + API responses)
axios1.xHTTP client

Project Structure

src/
β”œβ”€β”€ api/                  # API client and typed endpoint functions
β”‚   β”œβ”€β”€ client.ts         # Axios instance with interceptors
β”‚   β”œβ”€β”€ auth.ts           # Auth-related API functions
β”‚   └── [resource].ts     # Per-resource API functions
β”œβ”€β”€ components/           # Reusable UI components
β”‚   β”œβ”€β”€ ui/               # Primitive UI components (Button, Input, Modal, etc.)
β”‚   β”œβ”€β”€ layout/           # Layout components (Header, Sidebar, Footer, PageWrapper)
β”‚   └── [feature]/        # Feature-specific composed components
β”œβ”€β”€ context/              # React Context providers
β”‚   β”œβ”€β”€ AuthContext.tsx    # Auth state (user, token, login/logout)
β”‚   └── [Name]Context.tsx # Feature-specific global state
β”œβ”€β”€ hooks/                # Custom hooks
β”‚   β”œβ”€β”€ useAuth.ts        # Auth hook (wraps AuthContext)
β”‚   β”œβ”€β”€ useApi.ts         # Generic API hook patterns
β”‚   └── use[Name].ts      # Feature-specific hooks
β”œβ”€β”€ pages/                # Page-level components (1:1 with routes)
β”‚   β”œβ”€β”€ HomePage.tsx
β”‚   β”œβ”€β”€ LoginPage.tsx
β”‚   └── [Feature]Page.tsx
β”œβ”€β”€ routes/               # Route definitions
β”‚   β”œβ”€β”€ index.tsx         # Route tree with lazy loading
β”‚   └── ProtectedRoute.tsx # Auth guard wrapper
β”œβ”€β”€ styles/               # Global styles
β”‚   β”œβ”€β”€ globals.css       # Tailwind directives and global overrides
β”‚   └── tailwind.css      # @tailwind base/components/utilities
β”œβ”€β”€ types/                # Shared TypeScript types
β”‚   β”œβ”€β”€ api.ts            # API request/response types (mirrors backend structs)
β”‚   β”œβ”€β”€ models.ts         # Domain model types
β”‚   └── common.ts         # Shared utility types
β”œβ”€β”€ utils/                # Pure utility functions
β”‚   β”œβ”€β”€ format.ts         # Date, currency, string formatters
β”‚   β”œβ”€β”€ validation.ts     # Zod schemas shared across forms
β”‚   └── constants.ts      # App-wide constants
β”œβ”€β”€ App.tsx               # Root component with providers
β”œβ”€β”€ main.tsx              # Entry point (ReactDOM.createRoot)
└── vite-env.d.ts         # Vite environment type declarations

Key Patterns

1. Functional Components with TypeScript

Every component is a typed functional component. Never use class components (except ErrorBoundary). Always define a Props interface.

interface UserCardProps {
  user: User;
  onSelect?: (userId: string) => void;
  variant?: "compact" | "full";
}

export function UserCard({ user, onSelect, variant = "full" }: UserCardProps) {
  return (
    <div className="rounded-lg border border-gray-200 p-4">
      <h3 className="text-lg font-semibold">{user.name}</h3>
      {variant === "full" && <p className="text-sm text-gray-600">{user.email}</p>}
      {onSelect && (
        <button
          onClick={() => onSelect(user.id)}
          className="mt-2 rounded bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-700"
        >
          Select
        </button>
      )}
    </div>
  );
}

2. Custom Hooks for Logic Extraction

Extract any non-trivial logic (API calls, computed state, side effects) into custom hooks. Components should contain only rendering logic.

// hooks/useUsers.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { getUsers, createUser } from "@/api/users";
import type { CreateUserRequest } from "@/types/api";

export function useUsers() {
  return useQuery({
    queryKey: ["users"],
    queryFn: getUsers,
  });
}

export function useCreateUser() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (data: CreateUserRequest) => createUser(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["users"] });
    },
  });
}

3. API Client with Typed Functions

The API client is a configured axios instance. Each resource gets its own file with typed functions that match the Go backend contracts exactly.

// api/client.ts
import axios from "axios";

export const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_URL || "/api",
  headers: { "Content-Type": "application/json" },
});

apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem("token");
      window.location.href = "/login";
    }
    return Promise.reject(error);
  }
);
// api/users.ts
import { apiClient } from "./client";
import type { User, CreateUserRequest, UpdateUserRequest, PaginatedResponse } from "@/types/api";

export async function getUsers(params?: { page?: number; limit?: number }): Promise<PaginatedResponse<User>> {
  const { data } = await apiClient.get("/users", { params });
  return data;
}

export async function getUserById(id: string): Promise<User> {
  const { data } = await apiClient.get(`/users/${id}`);
  return data;
}

export async function createUser(req: CreateUserRequest): Promise<User> {
  const { data } = await apiClient.post("/users", req);
  return data;
}

export async function updateUser(id: string, req: UpdateUserRequest): Promise<User> {
  const { data } = await apiClient.put(`/users/${id}`, req);
  return data;
}

export async function deleteUser(id: string): Promise<void> {
  await apiClient.delete(`/users/${id}`);
}

4. TypeScript Types Matching Backend Contracts

Types in src/types/api.ts must mirror the Go backend structs field-for-field. Use the PRD/design doc API contracts as the single source of truth. JSON field names from Go struct tags become TypeScript property names.

// types/api.ts -- mirrors Go structs exactly
export interface User {
  id: string;
  email: string;
  name: string;
  role: "admin" | "user";
  created_at: string;   // ISO 8601 from Go time.Time
  updated_at: string;
}

export interface CreateUserRequest {
  email: string;
  name: string;
  password: string;
  role?: "admin" | "user";
}

export interface PaginatedResponse<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
}

export interface ApiError {
  error: string;
  code: string;
  details?: Record<string, string>;
}

5. State Management Strategy

State TypeSolutionWhen to Use
Server stateTanStack QueryData from API (lists, details, search results)
Global client stateReact Context + useReducerAuth, theme, UI preferences, notifications
Local component stateuseStateForm inputs, toggles, modals, ephemeral UI state
URL stateReact Router (useSearchParams)Filters, pagination, active tabs
Form stateReact Hook FormAny form with validation

Never use Redux, Zustand, or other external state libraries. TanStack Query handles server state; React Context handles the rest.

6. Routing with Lazy Loading

// routes/index.tsx
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { lazy, Suspense } from "react";
import { ProtectedRoute } from "./ProtectedRoute";
import { AppLayout } from "@/components/layout/AppLayout";

const HomePage = lazy(() => import("@/pages/HomePage"));
const LoginPage = lazy(() => import("@/pages/LoginPage"));
const UsersPage = lazy(() => import("@/pages/UsersPage"));
const UserDetailPage = lazy(() => import("@/pages/UserDetailPage"));
const NotFoundPage = lazy(() => import("@/pages/NotFoundPage"));

function LazyPage({ children }: { children: React.ReactNode }) {
  return (
    <Suspense fallback={<div className="flex h-screen items-center justify-center">Loading...</div>}>
      {children}
    </Suspense>
  );
}

const router = createBrowserRouter([
  {
    path: "/login",
    element: <LazyPage><LoginPage /></LazyPage>,
  },
  {
    path: "/",
    element: <ProtectedRoute><AppLayout /></ProtectedRoute>,
    children: [
      { index: true, element: <LazyPage><HomePage /></LazyPage> },
      { path: "users", element: <LazyPage><UsersPage /></LazyPage> },
      { path: "users/:id", element: <LazyPage><UserDetailPage /></LazyPage> },
    ],
  },
  { path: "*", element: <LazyPage><NotFoundPage /></LazyPage> },
]);

export function AppRouter() {
  return <RouterProvider router={router} />;
}

7. Tailwind CSS Styling Conventions

  • Mobile-first responsive design: start with base styles, add sm:, md:, lg: breakpoints
  • Use Tailwind utility classes directly on elements; avoid @apply except in globals.css for base resets
  • Group utilities logically: layout > spacing > sizing > typography > colors > effects
  • Component variants via conditional classes with clsx or cn utility
import { clsx } from "clsx";

interface ButtonProps {
  variant?: "primary" | "secondary" | "danger";
  size?: "sm" | "md" | "lg";
  children: React.ReactNode;
  onClick?: () => void;
  disabled?: boolean;
}

export function Button({ variant = "primary", size = "md", children, onClick, disabled }: ButtonProps) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={clsx(
        "inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
        {
          "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500": variant === "primary",
          "bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500": variant === "secondary",
          "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500": variant === "danger",
          "px-3 py-1.5 text-sm": size === "sm",
          "px-4 py-2 text-sm": size === "md",
          "px-6 py-3 text-base": size === "lg",
          "cursor-not-allowed opacity-50": disabled,
        }
      )}
    >
      {children}
    </button>
  );
}

8. Form Handling with React Hook Form + Zod

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

const createUserSchema = z.object({
  name: z.string().min(2, "Name must be at least 2 characters"),
  email: z.string().email("Invalid email address"),
  password: z.string().min(8, "Password must be at least 8 characters"),
  role: z.enum(["admin", "user"]).default("user"),
});

type CreateUserFormData = z.infer<typeof createUserSchema>;

export function CreateUserForm({ onSubmit }: { onSubmit: (data: CreateUserFormData) => void }) {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<CreateUserFormData>({
    resolver: zodResolver(createUserSchema),
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
      <div>
        <label htmlFor="name" className="block text-sm font-medium text-gray-700">Name</label>
        <input
          id="name"
          {...register("name")}
          className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
        />
        {errors.name && <p className="mt-1 text-sm text-red-600">{errors.name.message}</p>}
      </div>
      {/* Repeat pattern for email, password, role fields */}
      <button
        type="submit"
        disabled={isSubmitting}
        className="w-full rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
      >
        {isSubmitting ? "Creating..." : "Create User"}
      </button>
    </form>
  );
}

9. Error Boundaries

Wrap page-level components in error boundaries. Provide a user-friendly fallback and a retry mechanism. This is the one allowed exception to the no class components rule.

import { Component, type ErrorInfo, type ReactNode } from "react";

interface ErrorBoundaryProps {
  children: ReactNode;
  fallback?: ReactNode;
}

interface ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
}

export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error("ErrorBoundary caught:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="flex min-h-[400px] flex-col items-center justify-center">
          <h2 className="text-xl font-semibold text-gray-900">Something went wrong</h2>
          <p className="mt-2 text-sm text-gray-600">{this.state.error?.message}</p>
          <button
            onClick={() => this.setState({ hasError: false, error: null })}
            className="mt-4 rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
          >
            Try Again
          </button>
        </div>
      );
    }
    return this.props.children;
  }
}

10. Environment Variables

All environment variables are accessed via import.meta.env (Vite convention). Custom variables must be prefixed with VITE_.

// vite-env.d.ts
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_TITLE: string;
  readonly VITE_ENABLE_MOCKS: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}
# .env
VITE_API_URL=http://localhost:8080/api
VITE_APP_TITLE=MyApp

11. Responsive Design (Mobile-First)

Always start with mobile styles as the default, then layer on larger breakpoints:

<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
  {items.map((item) => (
    <Card key={item.id} item={item} />
  ))}
</div>

Breakpoint reference: sm (640px), md (768px), lg (1024px), xl (1280px), 2xl (1536px).

12. Loading and Empty States

Every data-fetching component must handle four states: loading, error, empty, and success.

export function UsersList() {
  const { data, isLoading, error } = useUsers();

  if (isLoading) return <LoadingSpinner />;
  if (error) return <ErrorMessage error={error} />;
  if (!data?.data.length) return <EmptyState message="No users found" />;

  return (
    <div className="space-y-4">
      {data.data.map((user) => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  );
}

Conventions

  1. No any type -- use unknown and narrow, or define proper types. The only acceptable any is in third-party library type workarounds (document with // eslint-disable-next-line comment).
  2. Named exports for all components and hooks. Default exports only for page components (required for React.lazy).
  3. File naming: PascalCase for components (UserCard.tsx), camelCase for hooks/utils (useAuth.ts, format.ts).
  4. One component per file unless tightly coupled sub-components are small (< 30 lines).
  5. Props interface must be defined directly above the component, named [ComponentName]Props.
  6. Barrel exports (index.ts) only at the top level of components/ui/, hooks/, utils/. Not in feature directories.
  7. Absolute imports using @/ path alias configured in vite.config.ts and tsconfig.json.
  8. No inline styles. Use Tailwind classes exclusively.
  9. Accessibility basics: all <img> have alt, all interactive elements are focusable, form inputs have <label>, use semantic HTML (<nav>, <main>, <section>, <article>).
  10. Every API response type must have a corresponding zod schema if used in form validation.

Vite Configuration

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  server: {
    port: 3000,
    proxy: {
      "/api": {
        target: "http://localhost:8080",
        changeOrigin: true,
      },
    },
  },
});

Knowledge Strategy

  • Patterns to capture: Reusable component patterns (data tables, forms, modals), API integration patterns, complex TanStack Query configurations (optimistic updates, infinite scroll), auth flow patterns.
  • Examples to collect: Successful component implementations, tricky TypeScript type patterns, responsive layout solutions, error handling patterns.
  • Update permission: Agents may freely add/update files in references/. Changes to SKILL.md or scripts/ require user approval.

Gives 0 of the 12 instructions most css styling skills give

Counted across 586 of the 596 authors here whose files we hold, read 2026-08-06

  • avoid excessive centered layoutsin 55 of 586, across 12 files
  • bundle code into single HTML filein 54 of 586, across 14 files
  • Respect prefers-reduced-motion user settingsin 52 of 586, across 35 files
  • avoid purple gradientsin 51 of 586, across 11 files
  • avoid uniform rounded cornersin 51 of 586, across 11 files
  • avoid Inter fontin 51 of 586, across 11 files
  • edit generated files to develop artifactin 50 of 586, across 10 files
  • animate only transform and opacity propertiesin 43 of 586
  • Make touch targets at least 44x44 pixelsin 41 of 586, across 15 files
  • Ensure minimum color contrast of 4.5:1in 39 of 586, across 10 files
  • use tailwind cssin 39 of 586, across 24 files
  • Use SVG icons instead of emojisin 38 of 586, across 11 files

Said here and by no other author read

  • Always define a Props interface
  • Extract non-trivial logic into custom hooks
  • Store API functions in dedicated resource files
  • Mirror backend structs field-for-field in types
  • Use React Context for global client state
  • Use Tailwind utility classes directly on elements

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.