nextjs-tailwind-guidelines

zapPilot's avatarfrom zapPilot

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.

⭐0stars🔀0forks📁View on GitHub🕐Updated Jan 2, 2026

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.
namenextjs-tailwind-guidelines
descriptionNext.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
  • useCallback for 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

// 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

  1. Server Components First - Default to Server, add 'use client' only when needed
  2. Suspense for Loading - Use Suspense boundaries, not early returns
  3. Tailwind for Styling - Utility-first CSS with design tokens
  4. Features Organized - components/, hooks/, lib/, types/ subdirs
  5. Next.js Image & Font - Always use optimized components
  6. Import Aliases - Use @/ for clean imports
  7. No Early Returns - Prevents layout shift
  8. TypeScript Strict - Type everything properly

Skill Status: Adapted for Next.js 15 + Tailwind CSS v4