Agent skill

frontend-react

Modern React stack: React 19, TypeScript, Tailwind CSS, Vite, TanStack Query. Use when: building React apps, components, state management, or UI. Triggers: "react", "frontend", "tailwind", "vite", "typescript react", "component", "useState", "tanstack", "react query".

Stars 0
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/timequity/vibe-coder/tree/main/skills/frontend-react

SKILL.md

React Frontend Stack

Live docs: Add use context7 to prompt for up-to-date React, TanStack Query, Tailwind documentation.

Quick Reference

Topic Reference
Components components.md — Button, Input, Modal, patterns
State state.md — useState, Zustand, Context, URL state

Tooling (2025)

Tool Purpose Why
Vite Build tool Fast HMR, ESM native
React 19 UI library RSC, Actions, use()
TypeScript Type safety Strict mode
Tailwind v4 Styling Utility-first, Vite plugin
TanStack Query Data fetching Caching, mutations
Zustand State Simple, no boilerplate
React Router 7 Routing Data loading, actions

Notes:

  • Tailwind v4: new config via Vite plugin, no tailwind.config.js
  • TanStack Router may require React 18.3.1 (use --legacy-peer-deps if needed)

Project Setup

bash
pnpm create vite@latest my-app --template react-ts
cd my-app
pnpm add @tanstack/react-query zustand
pnpm add -D tailwindcss @tailwindcss/vite

Vite Config

typescript
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: { '@': '/src' },
  },
});

Tailwind v4

css
/* src/index.css */
@import "tailwindcss";

Project Structure

src/
├── main.tsx             # Entry point
├── App.tsx              # Root component
├── index.css            # Tailwind imports
├── components/
│   ├── ui/              # Reusable UI (Button, Input, Card)
│   └── features/        # Feature components
├── pages/               # Route components
├── hooks/               # Custom hooks
├── stores/              # Zustand stores
├── api/                 # API client, queries
├── types/               # TypeScript types
└── lib/                 # Utilities

Component Patterns

Functional Component

tsx
interface UserCardProps {
  user: User;
  onEdit?: (id: string) => void;
}

export function UserCard({ user, onEdit }: UserCardProps) {
  return (
    <div className="rounded-lg border p-4">
      <h3 className="font-semibold">{user.name}</h3>
      <p className="text-sm text-gray-600">{user.email}</p>
      {onEdit && (
        <button
          onClick={() => onEdit(user.id)}
          className="mt-2 text-blue-600 hover:underline"
        >
          Edit
        </button>
      )}
    </div>
  );
}

Component with Children

tsx
interface CardProps {
  title: string;
  children: React.ReactNode;
  className?: string;
}

export function Card({ title, children, className }: CardProps) {
  return (
    <div className={cn("rounded-lg border bg-white p-6", className)}>
      <h2 className="mb-4 text-lg font-semibold">{title}</h2>
      {children}
    </div>
  );
}

Polymorphic Component

tsx
type ButtonProps<T extends React.ElementType = 'button'> = {
  as?: T;
  variant?: 'primary' | 'secondary';
  children: React.ReactNode;
} & React.ComponentPropsWithoutRef<T>;

export function Button<T extends React.ElementType = 'button'>({
  as,
  variant = 'primary',
  children,
  className,
  ...props
}: ButtonProps<T>) {
  const Component = as || 'button';
  return (
    <Component
      className={cn(
        'px-4 py-2 rounded-md font-medium',
        variant === 'primary' && 'bg-blue-600 text-white',
        variant === 'secondary' && 'bg-gray-200 text-gray-800',
        className
      )}
      {...props}
    >
      {children}
    </Component>
  );
}

// Usage
<Button>Click me</Button>
<Button as="a" href="/about">Link</Button>

Hooks

Custom Hook Example

tsx
function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue] as const;
}

useDebounce

tsx
function useDebounce<T>(value: T, delay: number): T {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debounced;
}

TanStack Query

Setup

tsx
// main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 1000,
      retry: 1,
    },
  },
});

<QueryClientProvider client={queryClient}>
  <App />
</QueryClientProvider>

Query Hook

tsx
// api/users.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

export function useUsers() {
  return useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then(r => r.json()),
  });
}

export function useUser(id: string) {
  return useQuery({
    queryKey: ['users', id],
    queryFn: () => fetch(`/api/users/${id}`).then(r => r.json()),
    enabled: !!id,
  });
}

export function useCreateUser() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: CreateUser) =>
      fetch('/api/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      }).then(r => r.json()),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
}

Usage in Component

tsx
function UserList() {
  const { data: users, isLoading, error } = useUsers();
  const createUser = useCreateUser();

  if (isLoading) return <Spinner />;
  if (error) return <Error message={error.message} />;

  return (
    <div>
      {users.map(user => <UserCard key={user.id} user={user} />)}
      <button onClick={() => createUser.mutate({ name: 'New' })}>
        Add User
      </button>
    </div>
  );
}

Zustand State

tsx
// stores/auth.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface AuthState {
  user: User | null;
  token: string | null;
  login: (user: User, token: string) => void;
  logout: () => void;
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      user: null,
      token: null,
      login: (user, token) => set({ user, token }),
      logout: () => set({ user: null, token: null }),
    }),
    { name: 'auth-storage' }
  )
);

// Usage
const { user, login, logout } = useAuthStore();

Forms

tsx
import { useState, FormEvent } from 'react';

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [errors, setErrors] = useState<Record<string, string>>({});

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault();
    setErrors({});

    if (!email) {
      setErrors(prev => ({ ...prev, email: 'Required' }));
      return;
    }

    // Submit...
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <div>
        <input
          type="email"
          value={email}
          onChange={e => setEmail(e.target.value)}
          className="w-full rounded border px-3 py-2"
          placeholder="Email"
        />
        {errors.email && <p className="text-sm text-red-500">{errors.email}</p>}
      </div>
      <button type="submit" className="w-full rounded bg-blue-600 py-2 text-white">
        Login
      </button>
    </form>
  );
}

Anti-patterns

Don't Do Instead
CRA (Create React App) Vite
CSS Modules / styled-components Tailwind
Redux (complex) Zustand (simple)
useEffect for data fetching TanStack Query
Prop drilling Context or Zustand
any types Proper TypeScript types
Index as key Unique ID as key
Inline object props useMemo or extract

Expand your agent's capabilities with these related and highly-rated skills.

timequity/vibe-coder

mvp-help

Help and documentation for Idea to MVP plugin. Use when: user asks about building MVPs, vibe coding, or available commands. Triggers: "help", "what can you do", "mvp help", "how to build".

0 0
Explore
timequity/vibe-coder

verification-gate

Hidden quality gate that runs before showing "Done!" to user - ensures all tests pass, build succeeds, and requirements met before claiming completion

0 0
Explore
timequity/vibe-coder

brainstorming

Refine ideas into detailed designs through Socratic dialogue. Use when: user has rough idea, needs to clarify requirements, explore approaches. Triggers: "brainstorm", "discuss idea", "I'm thinking about", "what if", "help me think through", "explore options", "/brainstorm".

0 0
Explore
timequity/vibe-coder

subagent-creator

Guide for creating effective subagents (custom agents). Use when users want to create a new subagent that can be dispatched via Task tool for autonomous work. Covers frontmatter fields (name, description, tools, model, permissionMode, skills), prompt design, and when to use subagents vs skills.

0 0
Explore
timequity/vibe-coder

backend-rust

Modern Rust backend with Axum, SQLx, tokio + CI/CD automation. Use when: building Rust APIs, high-performance services, or needing build/test/lint/audit automation. Triggers: "axum", "rust backend", "rust api", "sqlx", "tokio", "cargo build", "cargo test", "clippy", "rustfmt", "cargo-audit", "cross-compile", "rust ci", "release build", "rust security", "shuttle", "actix".

0 0
Explore
timequity/vibe-coder

test-driven-development

Write failing test first, then minimal code to pass. Red-Green-Refactor cycle. Use when: implementing features, fixing bugs, refactoring code. Triggers: "implement", "add feature", "fix bug", "tdd", "test first", "write tests", "test-driven".

0 0
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results