nextjs-tailwind-guidelines
Next.js 15 + Tailwind CSS development guidelines. Modern patterns including App Router, Server/Client Components, Suspense, lazy loading, file organization, Tailwind CSS styling, performance optimization, and TypeScript best practices. Use when creating components, pages, features, styling, routing, or working with Next.js code.
When & Why to Use This Skill
This Claude skill provides comprehensive development guidelines for Next.js 15 and Tailwind CSS, focusing on modern patterns like the App Router, Server and Client Component separation, and performance optimization. It helps developers implement best practices for TypeScript, file organization, and efficient styling to build scalable, high-performance web applications.
Use Cases
- Architecting new web applications using Next.js 15 App Router and standardized directory conventions.
- Optimizing React performance by correctly implementing Server Components and Suspense boundaries to reduce client-side JavaScript.
- Developing responsive and accessible UI components using Tailwind CSS utility classes and Framer Motion animations.
- Refactoring existing React codebases to follow modern TypeScript best practices and Next.js 15 features.
- Setting up efficient data fetching patterns, error handling, and loading states in a full-stack React environment.
| name | nextjs-tailwind-guidelines |
|---|---|
| description | Next.js 15 + Tailwind CSS development guidelines. Modern patterns including App Router, Server/Client Components, Suspense, lazy loading, file organization, Tailwind CSS styling, performance optimization, and TypeScript best practices. Use when creating components, pages, features, styling, routing, or working with Next.js code. |
Next.js + Tailwind Development Guidelines
Purpose
Comprehensive guide for modern Next.js 15 development with App Router, emphasizing Server Components, Suspense-based patterns, Tailwind CSS styling, and performance optimization.
When to Use This Skill
- Creating new components or pages
- Building new features with App Router
- Server vs Client Component decisions
- Styling with Tailwind CSS
- Next.js routing and layouts
- Performance optimization
- Organizing frontend code
- TypeScript best practices
Quick Start
New Component Checklist
- Server Component (default) or Client Component ('use client')?
- Use TypeScript with proper prop types
- Lazy load heavy components:
lazy(() => import()) - Wrap in
<Suspense>for loading states - Import alias:
@/for src directory - Tailwind classes with
className -
useCallbackfor event handlers passed to children - Default export at bottom
- No early returns with loading spinners
New Feature Checklist
- Create
src/features/{feature}/directory - Subdirectories:
components/,hooks/,lib/,types/ - TypeScript types in
types/ - Route in
src/app/{feature}/page.tsx - Lazy load client components when needed
- Suspense boundaries for async operations
- Export public API from
index.ts
Server vs Client Components
SERVER COMPONENTS (Default):
- Data fetching
- Static content
- SEO-critical content
- No interactivity needed
- Zero JavaScript to client
CLIENT COMPONENTS ('use client'):
- useState, useEffect, useContext
- Event handlers (onClick, onChange)
- Browser APIs (localStorage, window)
- Third-party libraries requiring browser
Key Rule: Default to Server Components, add 'use client' only when needed
Component Patterns
Server Component
// Server Component - No 'use client' needed
import { Suspense } from 'react';
interface Props {
id: string;
}
async function MyServerComponent({ id }: Props) {
// Fetch data directly in Server Component
const data = await fetch(`https://api.example.com/data/${id}`).then(r => r.json());
return (
<div className="p-4 bg-white dark:bg-gray-900">
<h1 className="text-2xl font-bold">{data.title}</h1>
</div>
);
}
export default MyServerComponent;
Client Component
'use client';
import { useState, useCallback } from 'react';
import { motion } from 'framer-motion';
interface Props {
onAction?: () => void;
}
export function MyClientComponent({ onAction }: Props) {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(c => c + 1);
onAction?.();
}, [onAction]);
return (
<motion.button
onClick={handleClick}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
whileHover={{ scale: 1.05 }}
>
Clicked {count} times
</motion.button>
);
}
export default MyClientComponent;
Tailwind CSS Styling
Basic Patterns
// Utility classes
<div className="flex items-center justify-between p-4 bg-white dark:bg-gray-900">
// Responsive
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
// Conditional classes (simple)
<div className={`base-class ${isActive ? 'active' : 'inactive'}`}>
// Conditional classes (complex) - use cn utility
import { cn } from '@/lib/utils';
<div className={cn(
"base-class",
isActive && "active-class",
isLarge && "text-xl"
)}>
Common Patterns
// Card
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
// Button
<button className="inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90">
// Input
<input className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm">
App Router Structure
src/
app/
layout.tsx # Root layout
page.tsx # Home page (/)
about/
page.tsx # /about
blog/
layout.tsx # Blog layout
page.tsx # /blog
[slug]/
page.tsx # /blog/[slug]
api/
hello/
route.ts # API endpoint
Page Example
// src/app/my-route/page.tsx
import { Suspense } from 'react';
import { MyFeature } from '@/features/my-feature';
// Metadata for SEO
export const metadata = {
title: 'My Page',
description: 'Page description',
};
export default function MyPage() {
return (
<div className="container mx-auto py-8">
<Suspense fallback={<div className="animate-pulse">Loading...</div>}>
<MyFeature />
</Suspense>
</div>
);
}
Layout Example
// src/app/layout.tsx
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'My App',
description: 'App description',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className="min-h-screen bg-background font-sans antialiased">
{children}
</body>
</html>
);
}
Data Fetching
Server Component (Recommended)
// Fetch directly in Server Component
async function MyComponent() {
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 } // Cache for 1 hour
}).then(r => r.json());
return <div>{data.title}</div>;
}
Client Component (when needed)
'use client';
import { useEffect, useState } from 'react';
export function MyClientData() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/data')
.then(r => r.json())
.then(setData)
.finally(() => setLoading(false));
}, []);
if (loading) return <div>Loading...</div>;
return <div>{data?.title}</div>;
}
Performance Optimization
Next.js Built-in
// Image optimization
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="Hero"
width={800}
height={600}
priority // For above-fold images
/>
// Font optimization
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
// Dynamic imports (lazy loading)
import dynamic from 'next/dynamic';
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <div>Loading...</div>,
ssr: false // Disable SSR if needed
});
React Optimization
'use client';
import { useMemo, useCallback, memo } from 'react';
// useMemo for expensive computations
const filtered = useMemo(() =>
items.filter(item => item.active).sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// useCallback for event handlers
const handleClick = useCallback((id: string) => {
console.log(id);
}, []);
// memo for expensive components
const ExpensiveComponent = memo(({ data }: Props) => {
return <div>{/* Heavy rendering */}</div>;
});
Loading & Error States
Loading UI
// src/app/my-route/loading.tsx
export default function Loading() {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary" />
</div>
);
}
Error Handling
// src/app/my-route/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h2 className="text-2xl font-bold mb-4">Something went wrong!</h2>
<button
onClick={reset}
className="px-4 py-2 bg-primary text-primary-foreground rounded"
>
Try again
</button>
</div>
);
}
File Organization
src/
app/ # Next.js App Router
layout.tsx
page.tsx
globals.css
components/ # Reusable UI components
ui/
button.tsx
card.tsx
layout/
header.tsx
footer.tsx
features/ # Feature-specific code
auth/
components/
hooks/
lib/
types/
index.ts
lib/ # Shared utilities
utils.ts
types/ # Global types
index.ts
TypeScript Best Practices
// Explicit prop types
interface ComponentProps {
title: string;
count?: number;
onAction: (id: string) => void;
}
// Type imports
import type { ReactNode } from 'react';
// Async Server Component typing
async function MyComponent(): Promise<JSX.Element> {
const data = await fetchData();
return <div>{data}</div>;
}
// Client Component typing
'use client';
export function MyClient({ children }: { children: ReactNode }) {
return <div>{children}</div>;
}
Animation with Framer Motion
'use client';
import { motion } from 'framer-motion';
export function AnimatedCard() {
return (
<motion.div
className="p-6 bg-card rounded-lg"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
whileHover={{ scale: 1.02 }}
>
Content
</motion.div>
);
}
Icons with Lucide React
import { Search, Menu, X, ChevronRight } from 'lucide-react';
export function IconExample() {
return (
<button className="flex items-center gap-2">
<Search className="h-4 w-4" />
<span>Search</span>
</button>
);
}
Core Principles
- Server Components First - Default to Server, add 'use client' only when needed
- Suspense for Loading - Use Suspense boundaries, not early returns
- Tailwind for Styling - Utility-first CSS with design tokens
- Features Organized - components/, hooks/, lib/, types/ subdirs
- Next.js Image & Font - Always use optimized components
- Import Aliases - Use @/ for clean imports
- No Early Returns - Prevents layout shift
- TypeScript Strict - Type everything properly
Skill Status: Adapted for Next.js 15 + Tailwind CSS v4