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".
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 context7to 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-depsif needed)
Project Setup
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
// 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
/* 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
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
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
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
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
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
// 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
// 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
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
// 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
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 |
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
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".
verification-gate
Hidden quality gate that runs before showing "Done!" to user - ensures all tests pass, build succeeds, and requirements met before claiming completion
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".
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.
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".
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".
Didn't find tool you were looking for?