Agent skill

frontend-dev-guidelines

Frontend development guidelines for Next.js 15 + React 19 + TypeScript with Tailwind CSS and TanStack Query

Stars 1
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/fghaffar/claude-cursor-setup/tree/main/skills/frontend-dev-guidelines

SKILL.md

Frontend Development Guidelines

Tech Stack

  • Framework: Next.js 15 with App Router
  • UI Library: React 19
  • Language: TypeScript (strict mode)
  • Styling: Tailwind CSS
  • Data Fetching: TanStack Query (React Query)
  • State Management: Zustand (client state) + TanStack Query (server state)
  • Forms: React Hook Form + Zod validation
  • UI Components: [ADD YOUR COMPONENT LIBRARY - shadcn/ui, Radix, MUI, etc.]

Project Structure

frontend/
├── src/
│   ├── app/                    # Next.js App Router pages
│   │   ├── (auth)/            # Route groups
│   │   ├── api/               # API routes
│   │   ├── layout.tsx         # Root layout
│   │   └── page.tsx           # Home page
│   ├── components/
│   │   ├── ui/                # Base UI components
│   │   ├── forms/             # Form components
│   │   └── features/          # Feature-specific components
│   ├── hooks/                 # Custom React hooks
│   ├── lib/
│   │   ├── api/              # API client functions
│   │   ├── utils/            # Utility functions
│   │   └── validations/      # Zod schemas
│   ├── providers/            # Context providers
│   ├── stores/               # Zustand stores
│   └── types/                # TypeScript type definitions
├── public/                   # Static assets
└── tailwind.config.ts       # Tailwind configuration

Component Patterns

Server vs Client Components

tsx
// Server Component (default in Next.js 15)
// - No 'use client' directive
// - Can use async/await directly
// - Can access server-only resources
export default async function ServerComponent() {
  const data = await fetchData(); // Direct data fetching
  return <div>{data.title}</div>;
}

// Client Component
// - Requires 'use client' directive at TOP of file
// - Can use hooks, event handlers, browser APIs
'use client';

import { useState } from 'react';

export function ClientComponent() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

Component Structure

tsx
'use client';

import { useState, useCallback } from 'react';
import { useQuery } from '@tanstack/react-query';

// Types at top
interface ComponentProps {
  id: string;
  onSuccess?: () => void;
}

// Component
export function MyComponent({ id, onSuccess }: ComponentProps) {
  // 1. Hooks first
  const [state, setState] = useState<string>('');
  const { data, isLoading, error } = useQuery({
    queryKey: ['item', id],
    queryFn: () => fetchItem(id),
  });

  // 2. Callbacks
  const handleClick = useCallback(() => {
    // handle click
    onSuccess?.();
  }, [onSuccess]);

  // 3. Early returns for loading/error states
  if (isLoading) return <LoadingSpinner />;
  if (error) return <ErrorMessage error={error} />;

  // 4. Main render
  return (
    <div className="flex flex-col gap-4">
      {/* JSX */}
    </div>
  );
}

Data Fetching with TanStack Query

Query Setup

tsx
// lib/api/items.ts
export async function fetchItems(): Promise<Item[]> {
  const response = await fetch('/api/items');
  if (!response.ok) {
    throw new Error('Failed to fetch items');
  }
  return response.json();
}

// In component
const { data, isLoading, error, refetch } = useQuery({
  queryKey: ['items'],
  queryFn: fetchItems,
  staleTime: 5 * 60 * 1000, // 5 minutes
});

Mutations

tsx
const mutation = useMutation({
  mutationFn: createItem,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['items'] });
    toast.success('Item created!');
  },
  onError: (error) => {
    toast.error(error.message);
  },
});

Styling with Tailwind

Best Practices

tsx
// Use semantic class groupings
<div className={cn(
  // Layout
  "flex flex-col gap-4",
  // Sizing
  "w-full max-w-md",
  // Spacing
  "p-4 m-2",
  // Colors
  "bg-white dark:bg-gray-900",
  // Typography
  "text-sm font-medium",
  // Borders
  "border rounded-lg",
  // Conditional
  isActive && "ring-2 ring-blue-500"
)}>

Utility Function for Class Names

tsx
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Form Handling

tsx
'use client';

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

const formSchema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
});

type FormData = z.infer<typeof formSchema>;

export function LoginForm() {
  const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
    resolver: zodResolver(formSchema),
  });

  const onSubmit = async (data: FormData) => {
    // Handle form submission
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} />
      {errors.email && <span>{errors.email.message}</span>}

      <input type="password" {...register('password')} />
      {errors.password && <span>{errors.password.message}</span>}

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Loading...' : 'Submit'}
      </button>
    </form>
  );
}

Error Handling

Error Boundary

tsx
'use client';

import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert" className="p-4 bg-red-50 border border-red-200 rounded">
      <h2>Something went wrong</h2>
      <pre className="text-sm">{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

// Usage
<ErrorBoundary FallbackComponent={ErrorFallback}>
  <MyComponent />
</ErrorBoundary>

API Error Handling

tsx
// lib/api/client.ts
export async function apiClient<T>(url: string, options?: RequestInit): Promise<T> {
  const response = await fetch(url, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...options?.headers,
    },
  });

  if (!response.ok) {
    const error = await response.json().catch(() => ({}));
    throw new ApiError(response.status, error.message || 'Request failed');
  }

  return response.json();
}

class ApiError extends Error {
  constructor(public status: number, message: string) {
    super(message);
    this.name = 'ApiError';
  }
}

TypeScript Best Practices

Type Definitions

tsx
// types/index.ts
export interface User {
  id: string;
  email: string;
  name: string;
  createdAt: Date;
}

// Partial types for updates
export type UserUpdate = Partial<Pick<User, 'email' | 'name'>>;

// API response types
export interface ApiResponse<T> {
  data: T;
  meta?: {
    total: number;
    page: number;
  };
}

Avoid These

tsx
// BAD
const data: any = await fetch(...);
const items = data as Item[];

// GOOD
const response = await fetch<ApiResponse<Item[]>>(...);
const items = response.data;

Performance Optimization

Code Splitting

tsx
// Dynamic imports for heavy components
import dynamic from 'next/dynamic';

const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
  loading: () => <ChartSkeleton />,
  ssr: false,
});

Memoization

tsx
// Memoize expensive computations
const processedData = useMemo(() => {
  return expensiveComputation(data);
}, [data]);

// Memoize callbacks passed to children
const handleClick = useCallback(() => {
  // handler logic
}, [dependencies]);

// Memoize components that receive object/array props
const MemoizedComponent = memo(MyComponent);

Common Patterns

Loading States

tsx
// Use Suspense for loading
import { Suspense } from 'react';

<Suspense fallback={<LoadingSkeleton />}>
  <AsyncComponent />
</Suspense>

Conditional Rendering

tsx
// Prefer early returns
if (!data) return null;
if (isLoading) return <Loading />;
if (error) return <Error />;

return <Content data={data} />;

CUSTOMIZE THIS FILE for your specific component library, design system, and project conventions.

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

fghaffar/claude-cursor-setup

github-code-reviewer

Review GitHub PRs with surgical precision. Flag only high-severity issues (bugs, security, performance, breaking changes) via succinct inline comments on specific lines. Skip style, nits, and minor improvements. High signal, low noise.

1 0
Explore
fghaffar/claude-cursor-setup

error-tracking

Add error tracking and performance monitoring to your project services. Use this skill when adding error handling, creating new controllers/routes, instrumenting background jobs, or tracking performance. Supports Sentry, Datadog, and other monitoring solutions. ALL ERRORS MUST BE CAPTURED - no exceptions.

1 0
Explore
fghaffar/claude-cursor-setup

skill-developer

Create and manage Claude Code skills following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns, file paths, content patterns), enforcement levels (block, suggest, warn), hook mechanisms (UserPromptSubmit, PreToolUse), session tracking, and the 500-line rule.

1 0
Explore
fghaffar/claude-cursor-setup

backend-dev-guidelines

Backend development guidelines for FastAPI + Python with PostgreSQL/pgvector and async patterns

1 0
Explore
mattpocock/skills

handoff

Compact the current conversation into a handoff document for another agent to pick up.

111,310 9,758
Explore
mattpocock/skills

obsidian-vault

Search, create, and manage notes in the Obsidian vault with wikilinks and index notes. Use when user wants to find, create, or organize notes in Obsidian.

111,310 9,758
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results