Back to catalog

nextjs-frontend-dev-guidelines

This repository contains a complete **Cursor (Claude AI)** infrastructure that provides **auto-activating skills**, **specialized agents**, and **powerful commands** to help you build better applications faster.

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

# Next.js Frontend Development Guidelines

**Skill Version:** 1.0.0  
**Stack:** Next.js 14+, React 18+, TypeScript, App Router  
**Purpose:** Guidelines for building Next.js frontend applications

## Overview

This skill provides comprehensive guidelines for Next.js frontend development using the App Router, React Server Components, and modern best practices.

## When to Use This Skill

Load this skill when working on:
- Next.js pages and layouts
- React components (client & server)
- Routing and navigation
- Data fetching patterns
- UI components and styling
- Performance optimization

## Quick Reference

### Project Structure
```
app/
├── (routes)/           # Route groups
├── api/               # API routes
├── components/        # Shared components
├── lib/              # Utilities
└── styles/           # Global styles

components/
├── ui/               # Reusable UI components
├── forms/            # Form components
└── layout/           # Layout components
```

### Key Principles

1. **Server Components First** - Default to Server Components, use Client Components only when needed
2. **Type Safety** - Full TypeScript coverage with proper interfaces
3. **Performance** - Code splitting, lazy loading, image optimization
4. **Accessibility** - ARIA labels, semantic HTML, keyboard navigation
5. **Error Handling** - Error boundaries and proper error states

## Core Guidelines

### 1. Component Architecture

#### Server Components (Default)
```typescript
// app/posts/page.tsx
import { Post } from '@/types';

async function getPosts(): Promise<Post[]> {
  const res = await fetch('https://api.example.com/posts', {
    cache: 'no-store', // or 'force-cache' for static
  });
  return res.json();
}

export default async function PostsPage() {
  const posts = await getPosts();
  
  return (
    <div>
      <h1>Posts</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
        </article>
      ))}
    </div>
  );
}
```

#### Client Components (When Needed)
```typescript
'use client';

import { useState } from 'react';

interface ButtonProps {
  onClick?: () => void;
  children: React.ReactNode;
}

export function InteractiveButton({ onClick, children }: ButtonProps) {
  const [count, setCount] = useState(0);

  return (
    <button
      onClick={() => {
        setCount(c => c + 1);
        onClick?.();
      }}
    >
      {children} (clicked {count} times)
    </button>
  );
}
```

**Use Client Components for:**
- Event handlers (onClick, onChange, etc.)
- State management (useState, useReducer)
- Effects (useEffect)
- Browser APIs (localStorage, geolocation)
- React hooks

### 2. File and Folder Conventions

#### Special Files
- `page.tsx` - Page component (creates route)
- `layout.tsx` - Shared layout
- `loading.tsx` - Loading UI
- `error.tsx` - Error handling
- `not-found.tsx` - 404 page
- `route.ts` - API route handler

#### Naming Conventions
- **Components:** PascalCase - `UserProfile.tsx`
- **Utilities:** camelCase - `formatDate.ts`
- **Types:** PascalCase - `UserTypes.ts`
- **Constants:** UPPER_SNAKE_CASE - `API_ENDPOINTS.ts`

### 3. Data Fetching

#### Server-Side Fetching
```typescript
// app/users/[id]/page.tsx
interface PageProps {
  params: { id: string };
}

async function getUser(id: string) {
  const res = await fetch(`https://api.example.com/users/${id}`, {
    next: { revalidate: 3600 } // ISR: revalidate every hour
  });
  
  if (!res.ok) throw new Error('Failed to fetch user');
  return res.json();
}

export default async function UserPage({ params }: PageProps) {
  const user = await getUser(params.id);
  
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}
```

#### Client-Side Fetching
```typescript
'use client';

import { useEffect, useState } from 'react';
import { User } from '@/types';

export function UserList() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(setUsers)
      .catch(err => setError(err.message))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}
```

### 4. Forms and User Input

```typescript
'use client';

import { useState, FormEvent } from 'react';

interface FormData {
  name: string;
  email: string;
}

export function ContactForm() {
  const [formData, setFormData] = useState<FormData>({
    name: '',
    email: '',
  });
  const [submitting, setSubmitting] = useState(false);

  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    setSubmitting(true);

    try {
      const res = await fetch('/api/contact', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(formData),
      });

      if (!res.ok) throw new Error('Submission failed');
      
      // Handle success
      alert('Form submitted!');
      setFormData({ name: '', email: '' });
    } catch (error) {
      console.error('Error:', error);
      alert('Failed to submit form');
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={formData.name}
        onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}
        placeholder="Name"
        required
      />
      <input
        type="email"
        value={formData.email}
        onChange={e => setFormData(prev => ({ ...prev, email: e.target.value }))}
        placeholder="Email"
        required
      />
      <button type="submit" disabled={submitting}>
        {submitting ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
}
```

### 5. Error Handling

```typescript
// app/posts/error.tsx
'use client';

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <p>{error.message}</p>
      <button onClick={reset}>Try again</button>
    </div>
  );
}
```

### 6. Loading States

```typescript
// app/posts/loading.tsx
export default function Loading() {
  return (
    <div>
      <div className="skeleton" />
      <div className="skeleton" />
      <div className="skeleton" />
    </div>
  );
}
```

## TypeScript Best Practices

### Component Props
```typescript
// Always define prop interfaces
interface ButtonProps {
  variant?: 'primary' | 'secondary';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  onClick?: () => void;
  children: React.ReactNode;
}

export function Button({ 
  variant = 'primary',
  size = 'md',
  disabled = false,
  onClick,
  children 
}: ButtonProps) {
  return (
    <button 
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}
```

### Generic Components
```typescript
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
  keyExtractor: (item: T) => string;
}

export function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map(item => (
        <li key={keyExtractor(item)}>
          {renderItem(item)}
        </li>
      ))}
    </ul>
  );
}
```

## Performance Optimization

### 1. Image Optimization
```typescript
import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Hero image"
  width={1200}
  height={600}
  priority // for above-the-fold images
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,..."
/>
```

### 2. Dynamic Imports
```typescript
import dynamic from 'next/dynamic';

const HeavyComponent = dynamic(() => import('@/components/HeavyComponent'), {
  loading: () => <p>Loading...</p>,
  ssr: false, // disable SSR if not needed
});
```

### 3. Memoization
```typescript
'use client';

import { memo, useMemo, useCallback } from 'react';

const ExpensiveComponent = memo(function ExpensiveComponent({ data }) {
  const processedData = useMemo(() => {
    return data.map(item => /* expensive operation */);
  }, [data]);

  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []);

  return <div onClick={handleClick}>{/* render */}</div>;
});
```

## Additional Resources

For more specific topics, refer to these resource files:
- `routing.md` - Advanced routing patterns
- `styling.md` - CSS and styling approaches
- `state-management.md` - Client state patterns
- `authentication.md` - Auth integration patterns

## Common Patterns

### Conditional Rendering
```typescript
{isLoading ? (
  <Spinner />
) : error ? (
  <ErrorMessage error={error} />
) : (
  <Content data={data} />
)}
```

### List Rendering
```typescript
{items.map(item => (
  <ListItem 
    key={item.id}
    item={item}
    onEdit={handleEdit}
    onDelete={handleDelete}
  />
))}
```

## Checklist for New Components

- [ ] TypeScript interfaces defined
- [ ] Proper naming (PascalCase for components)
- [ ] Client/Server component decision made
- [ ] Props validated and typed
- [ ] Error boundaries considered
- [ ] Loading states implemented
- [ ] Accessibility attributes added
- [ ] Performance optimized (memo, useMemo if needed)

---

**Last Updated:** 2025-01-10  
**Maintained By:** Development Team