A comprehensive collection of TypeScript patterns, best practices, and advanced React concepts implemented with Next.js 14. This repository demonstrates type-safe development, modern React patterns, and production-ready architectures.
- Overview
- Tech Stack
- Features
- Project Structure
- TypeScript Patterns
- Installation
- Key Learnings
- Best Practices
This repository showcases advanced TypeScript usage with React and Next.js, demonstrating real-world patterns, type safety, and modern development practices. It serves as both a learning resource and a reference implementation for building scalable, type-safe applications.
- Master TypeScript with React and Next.js
- Implement type-safe patterns and best practices
- Build performant, scalable applications
- Demonstrate advanced React patterns
- Showcase modern development workflows
- Next.js 14 - Full-stack React framework with App Router
- React 18 - UI library with Server Components
- TypeScript 5 - Type-safe JavaScript
- Tailwind CSS - Utility-first CSS framework
- React Hook Form - Type-safe forms with validation
- Zod - Schema validation
- TanStack Query - Data fetching and caching
- Zustand - Lightweight state management
- Framer Motion - Animation library
- React Testing Library - Component testing
- Jest - Test runner
- ESLint - Code quality
- Prettier - Code formatting
- Husky - Git hooks
- Commitlint - Commit message linting
- GitHub Actions - CI/CD
- π― Strict Type Checking - No
anytypes, full type safety - π§ Advanced Types - Generics, utility types, conditional types
- π Type Inference - Leveraging TypeScript's powerful inference
- ποΈ Type-Safe APIs - End-to-end type safety with tRPC/REST
- π¨ Component Patterns - Strongly typed components and hooks
- π App Router - File-based routing with layouts
- π Server Components - Optimized server-side rendering
- πΎ Server Actions - Type-safe server mutations
- π Streaming SSR - Progressive rendering
- πΌοΈ Image Optimization - Next/Image with lazy loading
- π― API Routes - Type-safe backend endpoints
- ποΈ Clean Architecture - Separation of concerns
- π§© Component Composition - Reusable, composable components
- π£ Custom Hooks - Encapsulated, reusable logic
- π Error Boundaries - Graceful error handling
- βΏ Accessibility - WCAG compliant components
react-ts-practice/
βββ app/ # Next.js App Router
β βββ (auth)/ # Route group for auth
β β βββ login/
β β βββ register/
β βββ (dashboard)/ # Protected routes
β β βββ layout.tsx
β β βββ dashboard/
β βββ api/ # API routes
β β βββ [...]/route.ts
β βββ layout.tsx # Root layout
β βββ page.tsx # Home page
βββ components/ # React components
β βββ ui/ # UI components
β β βββ Button/
β β β βββ Button.tsx
β β β βββ Button.test.tsx
β β β βββ index.ts
β β βββ Form/
β βββ features/ # Feature components
β β βββ auth/
β β βββ dashboard/
β βββ layouts/ # Layout components
βββ lib/ # Library code
β βββ api/ # API clients
β βββ auth/ # Authentication
β βββ db/ # Database utilities
β βββ utils/ # Utility functions
βββ hooks/ # Custom React hooks
β βββ useAuth.ts
β βββ useDebounce.ts
β βββ useLocalStorage.ts
βββ types/ # TypeScript types
β βββ api.ts
β βββ models.ts
β βββ global.d.ts
βββ services/ # Business logic
β βββ user.service.ts
β βββ product.service.ts
βββ store/ # State management
β βββ auth.store.ts
β βββ cart.store.ts
βββ styles/ # Global styles
β βββ globals.css
βββ tests/ # Test utilities
β βββ setup.ts
β βββ utils.tsx
βββ public/ # Static assets
βββ .env.example # Environment variables
βββ next.config.js # Next.js config
βββ tsconfig.json # TypeScript config
βββ tailwind.config.ts # Tailwind config
βββ package.json
// Strongly typed functional component
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
children: React.ReactNode;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
isLoading = false,
children,
onClick,
}) => {
return (
<button
className={cn(
'rounded-lg font-medium transition-all',
variants[variant],
sizes[size],
isLoading && 'opacity-50 cursor-not-allowed'
)}
onClick={onClick}
disabled={isLoading}
>
{isLoading ? <Spinner /> : children}
</button>
);
};// Type-safe custom hook
function useLocalStorage<T>(
key: string,
initialValue: T
): [T, (value: T | ((val: T) => T)) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(`Error loading ${key} from localStorage:`, error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(`Error saving ${key} to localStorage:`, error);
}
};
return [storedValue, setValue];
}// Schema validation with inference
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(2).max(50),
role: z.enum(['user', 'admin', 'moderator']),
createdAt: z.date(),
preferences: z.object({
theme: z.enum(['light', 'dark', 'system']),
notifications: z.boolean(),
}).optional(),
});
export type User = z.infer<typeof UserSchema>;
// Type-safe API function
export async function getUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
return UserSchema.parse(data);
}// Generic list component
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => React.ReactNode;
keyExtractor: (item: T) => string;
EmptyComponent?: React.ComponentType;
}
function List<T>({
items,
renderItem,
keyExtractor,
EmptyComponent
}: ListProps<T>) {
if (items.length === 0 && EmptyComponent) {
return <EmptyComponent />;
}
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>
{renderItem(item, index)}
</li>
))}
</ul>
);
}// app/actions/user.actions.ts
'use server';
import { z } from 'zod';
import { revalidatePath } from 'next/cache';
const UpdateUserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
});
export async function updateUser(
userId: string,
formData: FormData
): Promise<{ success: boolean; error?: string }> {
try {
const validatedData = UpdateUserSchema.parse({
name: formData.get('name'),
email: formData.get('email'),
});
await db.user.update({
where: { id: userId },
data: validatedData,
});
revalidatePath('/profile');
return { success: true };
} catch (error) {
if (error instanceof z.ZodError) {
return { success: false, error: error.errors[0].message };
}
return { success: false, error: 'Failed to update user' };
}
}- Node.js 18+
- npm/yarn/pnpm
- Git
-
Clone the repository
git clone https://github.com/jefercort/react-ts-practice.git cd react-ts-practice -
Install dependencies
npm install # or yarn install # or pnpm install
-
Set up environment variables
cp .env.example .env.local
-
Run development server
npm run dev # or yarn dev # or pnpm dev
-
Open browser
Navigate to http://localhost:3000
# Development
npm run dev # Start development server
npm run build # Build for production
npm run start # Start production server
# Type Checking
npm run type-check # Run TypeScript compiler check
npm run lint # Run ESLint
npm run lint:fix # Fix ESLint errors
# Testing
npm run test # Run tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Generate coverage report
# Formatting
npm run format # Format with Prettier
npm run format:check # Check formatting- Type Safety: Eliminating runtime errors through compile-time checks
- Type Inference: Leveraging TypeScript's powerful type inference
- Generics: Building reusable, type-safe components
- Utility Types: Using Partial, Required, Pick, Omit effectively
- Discriminated Unions: Handling complex state with type safety
- Compound Components: Building flexible, composable UIs
- Render Props: Sharing logic between components
- Higher-Order Components: Enhancing component functionality
- Custom Hooks: Extracting and reusing stateful logic
- Context with TypeScript: Type-safe context providers
- App Router: Modern routing with layouts and groups
- Server Components: Optimal data fetching and rendering
- Server Actions: Type-safe mutations without API routes
- Middleware: Request interception and modification
- Edge Runtime: Deploying to edge locations
- β Enable strict mode in tsconfig.json
- β
Avoid using
anytype - β Use type inference where possible
- β Create shared type definitions
- β Validate external data with Zod
- β Use functional components with hooks
- β Implement error boundaries
- β Optimize with React.memo and useMemo
- β Follow accessibility guidelines
- β Write comprehensive tests
- β Use Server Components by default
- β Implement proper loading states
- β Optimize images with next/image
- β Use dynamic imports for code splitting
- β Implement proper SEO with metadata
- Add E2E tests with Playwright
- Implement tRPC for end-to-end type safety
- Add Storybook for component documentation
- Implement Progressive Web App features
- Add internationalization (i18n)
- Implement real-time features with WebSockets
- Add monitoring with Sentry
- Implement feature flags
- Add GraphQL with type generation
- Create CI/CD pipeline with GitHub Actions
Contributions are welcome! Please read the contributing guidelines before submitting PRs.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Kevin Cortes
- GitHub: @jefercort
- LinkedIn: Kevin Cortes PRO
- Portfolio: PRO ENGINEERING
- Next.js team for the amazing framework
- TypeScript team for making JavaScript type-safe
- Vercel for hosting and deployment
- The React community for continuous innovation
β If you find this repository helpful, please give it a star!
π Live Demo | π Documentation | π Report Bug