Skip to content

314-code/nextjs-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

65 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Next.js Template

A modern, production-ready Next.js template with a comprehensive development setup featuring custom component generation, type-safe CSS modules, robust error handling, infinite scroll with adapter pattern, accessible UI components, and Storybook integration.

πŸ“‘ Table of Contents

πŸš€ Tech Stack

  • Framework: Next.js 15 with App Router
  • Language: TypeScript
  • Styling: CSS Modules with PostCSS
  • State Management: TanStack Query (React Query)
  • Testing: Vitest with React Testing Library
  • Component Development: Storybook
  • Linting/Formatting: Biome
  • Git Hooks: Husky with Commitlint
  • Package Manager: pnpm
  • Node Version: 22.20.0 (managed via Volta)

✨ Key Features

  • 🎨 Component Library - Pre-built, accessible UI components (Input, Select, Checkbox, Toast)
  • ♾️ Infinite Scroll - API-agnostic data fetching with adapter pattern
  • πŸͺ Custom Hooks - useDebounce, useInfiniteScroll, useKeyboardNavigation, and more
  • β™Ώ Accessibility - Screen reader support, keyboard navigation, ARIA compliance
  • πŸ“– Storybook - Interactive component development and documentation
  • πŸ›‘οΈ Type Safety - Full TypeScript support with strict mode
  • 🎭 Error Handling - Smart error boundaries with retry logic
  • 🎨 Styling System - CSS Modules with type-safe selectors and CSS variables
  • ⚑ Component Generator - CLI tool for rapid component scaffolding
  • πŸ§ͺ Testing - Vitest + React Testing Library with coverage reports
  • πŸ“ Code Quality - Biome for linting/formatting, Husky for git hooks

πŸ“‹ Prerequisites

  • Node.js 22.20.0 (or use Volta for automatic version management)
  • pnpm 8.x or higher

πŸ› οΈ Getting Started

Installation

# Install dependencies
pnpm install

# Start development server with Turbopack
pnpm dev

Open http://localhost:3000 to view the application.

Available Scripts

# Development
pnpm dev              # Start dev server with Turbopack
pnpm build            # Build for production
pnpm start            # Start production server

# Testing
pnpm test             # Run tests in watch mode
pnpm test:ui          # Run tests with UI
pnpm test:coverage    # Generate coverage report

# Storybook
pnpm storybook        # Start Storybook dev server
pnpm build-storybook  # Build Storybook for production

# Code Quality
pnpm format           # Check formatting
pnpm format:write     # Fix formatting
pnpm check            # Lint and check code
pnpm check:write      # Lint and auto-fix

# Component Generation
pnpm generate         # Interactive component generator

πŸ—οΈ Project Structure

β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/                                    # Next.js App Router pages
β”‚   β”‚   β”œβ”€β”€ layout.tsx                          # Root layout
β”‚   β”‚   └── page.tsx                            # Home page
β”‚   β”œβ”€β”€ components/                             # Reusable components
β”‚   β”‚   β”œβ”€β”€ Accessibility/                      # Accessibility components
β”‚   β”‚   β”‚   └── Announcer/                      # Screen reader announcers
β”‚   β”‚   β”‚       β”œβ”€β”€ Loading/                    # Loading state announcer
β”‚   β”‚   β”‚       └── Search/                     # Search result announcer
β”‚   β”‚   β”œβ”€β”€ shared/                             # Shared UI components
β”‚   β”‚   β”‚   β”œβ”€β”€ Input/                          # Input component with variants
β”‚   β”‚   β”‚   β”œβ”€β”€ Select/                         # Select dropdown component
β”‚   β”‚   β”‚   β”œβ”€β”€ Checkbox/                       # Checkbox component
β”‚   β”‚   β”‚   └── Toast/                          # Toast notification system
β”‚   β”‚   └── SmartErrorBoundary/                 # Error boundary with retry logic
β”‚   β”œβ”€β”€ hooks/                                  # Custom React hooks
β”‚   β”‚   β”œβ”€β”€ infinite-scroll/                    # Infinite scroll hooks
β”‚   β”‚   β”‚   β”œβ”€β”€ use-infinite-scroll.ts          # Core infinite scroll logic
β”‚   β”‚   β”‚   β”œβ”€β”€ use-infinite-products.ts        # Product-specific infinite scroll
β”‚   β”‚   β”‚   └── use-prefetch.ts                 # Data prefetching
β”‚   β”‚   β”œβ”€β”€ keyboard-navigation/                # Keyboard navigation hooks
β”‚   β”‚   β”‚   β”œβ”€β”€ use-keyboard-navigation.ts      # Core keyboard navigation
β”‚   β”‚   β”‚   └── use-grid-navigation.ts          # Grid navigation logic
β”‚   β”‚   β”œβ”€β”€ use-debounce.ts                     # Debounce hook
β”‚   β”‚   β”œβ”€β”€ use-filter-params.ts                # URL filter params management
β”‚   β”‚   └── use-infinite-data.ts                # API-agnostic infinite data fetching
β”‚   β”œβ”€β”€ adapters/                               # Data adapter pattern
β”‚   β”‚   β”œβ”€β”€ data-adapter.ts                     # Base adapter interface
β”‚   β”‚   β”œβ”€β”€ dummyjson-product-adapter.ts        # DummyJSON API adapter
β”‚   β”‚   β”œβ”€β”€ custom-backend-product-adapter.ts # Custom backend adapter
β”‚   β”‚   └── README.md                           # Adapter documentation
β”‚   β”œβ”€β”€ lib/                                    # Utility functions
β”‚   β”‚   β”œβ”€β”€ class-selectors.ts                  # Type-safe CSS module helpers
β”‚   β”‚   β”œβ”€β”€ api-client.ts                       # Fetch wrapper with error handling
β”‚   β”‚   └── api-client.example.ts               # API client usage examples
β”‚   β”œβ”€β”€ providers/                              # React context providers
β”‚   β”‚   β”œβ”€β”€ QueryProvider.tsx                   # TanStack Query setup
β”‚   β”‚   └── AnnouncementProvider.tsx            # Screen reader announcements
β”‚   └── styles/                                 # Global styles
β”‚       β”œβ”€β”€ globals.css                         # CSS variables & reset
β”‚       └── utilities.module.css                # Utility classes
β”œβ”€β”€ scripts/
β”‚   └── generate-component/                     # Component generator
β”‚       β”œβ”€β”€ index.ts                            # Generator CLI
β”‚       └── templates/                          # Component templates
β”œβ”€β”€ .storybook/                                 # Storybook configuration
β”‚   β”œβ”€β”€ main.ts                                 # Main Storybook config
β”‚   β”œβ”€β”€ preview.tsx                             # Global preview settings
β”‚   β”œβ”€β”€ manager.ts                              # Manager config
β”‚   └── theme.ts                                # Custom theme
└── public/                                     # Static assets

🎨 Component Generator

Generate new components quickly with the custom CLI:

# Basic server component
pnpm generate MyComponent

# Client component with styles
pnpm generate MyButton --client --styles

# Component with props
pnpm generate UserCard --props "name: string; age: number; email: string"

# Custom directory (relative to /src)
pnpm generate Header --directory "components/layout"

# Force overwrite existing component
pnpm generate MyComponent --force

Generator Options

  • -c, --client - Generate client component (default: server component)
  • -s, --styles - Generate CSS module file
  • -p, --props <props> - Define component props (format: "name: type; name2: type2")
  • -d, --directory <path> - Target directory relative to /src (default: components)
  • -f, --force - Overwrite existing component

Generated Files

Each component includes:

  • component-name.tsx - Component file
  • component-name.module.css - CSS module (if --styles flag used)
  • index.ts - Barrel export for cleaner imports

🎯 Code Conventions

CSS Modules

Type-safe CSS module usage with custom selectors:

import { createStrictClassSelector } from "@/lib/class-selectors";
import styles from "./component.module.css";

const css = createStrictClassSelector(styles);

function Component() {
    return <div className={css("container")}>Content</div>;
}

Utility Classes

Reusable utility classes are available in utilities.module.css:

import utilities from "@/styles/utilities.module.css";

const cssUtils = createStrictClassSelector(utilities);

<div className={cssUtils("flex", "itemsCenter")} />;

Error Boundaries

Use SmartErrorBoundary for robust error handling:

import SmartErrorBoundary from "@/components/SmartErrorBoundary";

<SmartErrorBoundary context="UserProfile" level="component" maxRetries={3} enableNavigation={false}>
    <UserProfile />
</SmartErrorBoundary>;

Levels:

  • component - Local component errors
  • page - Page-level errors (adds navigation buttons)
  • app - Application-level errors (adds reload/home buttons)

πŸͺ Custom Hooks

Data Fetching & Infinite Scroll

useInfiniteData

API-agnostic infinite data fetching with adapter pattern:

import { useInfiniteData } from "@/hooks/use-infinite-data";
import { DummyJSONProductAdapter } from "@/adapters/dummyjson-product-adapter";
import { fetcher } from "@/lib/api-client";

const adapter = new DummyJSONProductAdapter(20);

function ProductList() {
    const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteData({
        queryKey: ["products"],
        adapter,
        fetcher,
        filters: { search: "phone", category: "smartphones" },
    });

    return (
        <div>
            {data?.items.map((product) => (
                <div key={product.id}>{product.title}</div>
            ))}
            {hasNextPage && (
                <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
                    Load More
                </button>
            )}
        </div>
    );
}

useInfiniteScroll

Automatic infinite scrolling with IntersectionObserver:

import { useInfiniteScroll } from "@/hooks/infinite-scroll/use-infinite-scroll";

function InfiniteList() {
    const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteData({
        /* ... */
    });

    const { triggerRef } = useInfiniteScroll({
        hasNextPage,
        isFetchingNextPage,
        onLoadMore: fetchNextPage,
        rootMargin: "200px", // Load when 200px from bottom
        threshold: 0.1,
    });

    return (
        <div>
            {data?.items.map((item) => (
                <div key={item.id}>{item.title}</div>
            ))}
            {/* Trigger element for intersection observer */}
            <div ref={triggerRef} />
            {isFetchingNextPage && <div>Loading more...</div>}
        </div>
    );
}

State Management

useDebounce

Debounce rapidly changing values:

import { useDebounce } from "@/hooks/use-debounce";

function SearchInput() {
    const [search, setSearch] = useState("");
    const debouncedSearch = useDebounce(search, 500); // 500ms delay

    useEffect(() => {
        // Only runs after user stops typing for 500ms
        fetchResults(debouncedSearch);
    }, [debouncedSearch]);

    return <input value={search} onChange={(e) => setSearch(e.target.value)} />;
}

Keyboard Navigation

useKeyboardNavigation

Handle keyboard interactions with focus management:

import { useKeyboardNavigation } from "@/hooks/keyboard-navigation/use-keyboard-navigation";

function Modal({ onClose }) {
    const { containerRef } = useKeyboardNavigation({
        onEscape: onClose,
        onEnter: handleSubmit,
        trapFocus: true, // Keep focus within modal
        restoreFocus: true, // Restore focus when unmounted
    });

    return <div ref={containerRef}>{/* Modal content */}</div>;
}

useGridNavigation

Navigate items in a grid with arrow keys:

import { useGridNavigation } from "@/hooks/keyboard-navigation/use-grid-navigation";

function ProductGrid({ products }) {
    const { containerRef, focusedIndex } = useGridNavigation({
        itemCount: products.length,
        columns: 4,
        onSelect: (index) => openProduct(products[index]),
    });

    return (
        <div ref={containerRef}>
            {products.map((product, index) => (
                <div key={product.id} data-focused={focusedIndex === index}>
                    {product.title}
                </div>
            ))}
        </div>
    );
}

πŸ”Œ Data Adapters

The adapter pattern allows you to swap APIs without changing your application code. See the comprehensive Adapter Documentation for detailed examples.

Quick Start

// 1. Create an adapter for your API
import type { DataAdapter } from "@/adapters/data-adapter";

class MyAPIAdapter implements DataAdapter<Product, Filters, Response, number> {
    initialPageParam = 1;

    buildURL(filters: Filters, page: number): string {
        return `/api/products?page=${page}&search=${filters.search}`;
    }

    parseResponse(response: Response) {
        return {
            items: response.data,
            total: response.total,
            hasNextPage: response.hasMore,
        };
    }

    getNextPageParam(lastPage, allPages) {
        return lastPage.hasNextPage ? allPages.length + 1 : undefined;
    }
}

// 2. Use with useInfiniteData
const adapter = new MyAPIAdapter();
const { data } = useInfiniteData({ adapter, fetcher, filters });

Benefits:

  • πŸ”„ Swap APIs by changing adapter only
  • πŸ§ͺ Easy testing with mock adapters
  • πŸ“¦ Reusable across different data sources
  • πŸ›‘οΈ Full TypeScript support

Included Adapters:

  • DummyJSONProductAdapter - Skip-based pagination example
  • CustomBackendProductAdapter - Page-based pagination example

🎨 Shared UI Components

Input

Text input with variants and states:

import { Input } from "@/components/shared";

<Input
    variant="outlined" // outlined | filled | ghost
    size="medium" // small | medium | large
    error="Invalid email"
    placeholder="Enter email..."
/>;

Select

Dropdown select component:

import { Select } from "@/components/shared";

<Select
    options={[
        { value: "1", label: "Option 1" },
        { value: "2", label: "Option 2" },
    ]}
    onChange={(value) => console.log(value)}
    placeholder="Select an option"
/>;

Checkbox

Accessible checkbox component:

import { Checkbox } from "@/components/shared";

<Checkbox label="Accept terms" checked={accepted} onChange={setAccepted} />;

Toast Notifications

Display toast notifications with the toast system:

import { toast } from "@/components/shared/Toast";

// Add ToastProvider to your layout
import { ToastProvider } from "@/components/shared/Toast";

function RootLayout({ children }) {
    return (
        <ToastProvider maxToasts={5} position="top-right">
            {children}
        </ToastProvider>
    );
}

// Use toast in components
toast.success("Operation completed!");
toast.error("Something went wrong");
toast.info("New message received");
toast.warning("Please save your work");

// With custom duration and actions
toast.success("File uploaded", {
    duration: 5000,
    action: { label: "View", onClick: () => navigate("/files") },
});

β™Ώ Accessibility Features

AnnouncementProvider

Screen reader announcements for dynamic content:

import { AnnouncementProvider, useAnnouncement } from "@/providers/AnnouncementProvider";

// Add to root layout
function RootLayout({ children }) {
    return <AnnouncementProvider>{children}</AnnouncementProvider>;
}

// Use in components
function MyComponent() {
    const { announce } = useAnnouncement();

    const handleAction = () => {
        // Announce to screen readers
        announce("Item added to cart", "polite");
    };
}

Screen Reader Announcers

  • LoadingAnnouncer - Announces loading states
  • SearchAnnouncer - Announces search results count
import { LoadingAnnouncer } from "@/components/Accessibility/Announcer/Loading";
import { SearchAnnouncer } from "@/components/Accessibility/Announcer/Search";

<LoadingAnnouncer isLoading={isLoading} message="Loading products" />
<SearchAnnouncer resultCount={results.length} query={searchQuery} />

πŸ“– Storybook

Develop and document components in isolation:

# Start Storybook
pnpm storybook

# Build static Storybook
pnpm build-storybook

View component stories at http://localhost:6006

Features:

  • Interactive component development
  • Automatic documentation
  • Accessibility testing with a11y addon
  • Responsive viewport testing
  • Component interaction testing

All shared components include Storybook stories (.stories.tsx files).

πŸ§ͺ Testing

Tests are powered by Vitest and React Testing Library:

# Run tests
pnpm test

# Watch mode with UI
pnpm test:ui

# Generate coverage
pnpm test:coverage

Example test:

import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import Component from "./component";

describe("Component", () => {
    it("renders correctly", () => {
        render(<Component />);
        expect(screen.getByText("Hello")).toBeInTheDocument();
    });
});

πŸ“ Commit Conventions

This project uses Conventional Commits enforced by Commitlint.

Commit Format

<type>(<scope>): <description>

[optional body]

[optional footer]

Types

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation changes
  • style - Code style changes (formatting, etc.)
  • refactor - Code refactoring
  • perf - Performance improvements
  • test - Adding or updating tests
  • chore - Maintenance tasks
  • ci - CI/CD changes
  • build - Build system changes
  • revert - Revert previous commit

Scopes

Scopes must be UPPERCASE:

  • CORE - Core functionality
  • API - API related
  • UI - User interface
  • DB - Database
  • CONFIG - Configuration
  • AUTH - Authentication
  • SEARCH - Search functionality
  • CHECKOUT - Checkout process
  • CI - CI/CD
  • BUILD - Build process
  • Branch name (automatically detected)

Examples

git commit -m "feat(UI): add user profile component"
git commit -m "fix(API): resolve authentication timeout"
git commit -m "docs(CORE): update README with new features"

🎨 Styling System

CSS Custom Properties

All design tokens are defined in globals.css:

/* Colors */
--color-primary
--color-secondary
--color-text-primary
--color-text-secondary
--color-action-primary

/* Spacing */
--spacing-xs to --spacing-2xl

/* Border Radius */
--radius-sm to --radius-xl

/* Font Sizes */
--font-size-xs to --font-size-4xl

/* Font Weights */
--font-weight-normal to --font-weight-bold

Dark Mode

Dark mode is automatically supported via prefers-color-scheme:

@media (prefers-color-scheme: dark) {
    :root {
        --color-main: hsl(0, 0%, 10%);
        /* ... */
    }
}

πŸ”§ Configuration

Editor Setup (VS Code)

The project includes VS Code settings for:

  • Auto-format on save with Biome
  • Organize imports automatically
  • CSS validation
  • TypeScript integration

PostCSS

Configured with:

  • postcss-custom-media - Custom media queries
  • autoprefixer - Vendor prefixes

Biome Configuration

Format and lint settings in biome.json:

  • 120 character line width
  • Tabs for indentation (4 spaces)
  • Double quotes
  • ES5 trailing commas

πŸ“š Learn More

Next.js Resources

Additional Resources

🚒 Deployment

Vercel (Recommended)

The easiest way to deploy is using Vercel:

  1. Push your code to a Git repository
  2. Import your project to Vercel
  3. Vercel will auto-detect Next.js and configure deployment
  4. Your app will be live!

See Next.js deployment documentation for other hosting options.

🀝 Contributing

  1. Follow the commit conventions
  2. Run pnpm check:write before committing (automated via Husky)
  3. Write tests for new features
  4. Update documentation as needed

πŸ“„ License

This project is open source and available under the MIT License.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors