Code Style
TypeScript patterns and component structure
Modern TypeScript and React patterns used in Plainform.
TypeScript
Type Annotations
// Always annotate function params and returns
function formatDate(date: Date): string {
return new Intl.DateTimeFormat('en-US').format(date);
}
// Let TypeScript infer variables
const userName = 'John'; // string inferred
const count = 42; // number inferredInterfaces vs Types
// Interfaces for objects
interface IUser {
id: string;
name: string;
}
// Types for unions and complex types
type Status = 'pending' | 'active' | 'inactive';
type Result<T> = { success: true; data: T } | { success: false; error: string };Type Guards
function isError(value: unknown): value is Error {
return value instanceof Error;
}
if (typeof value === 'string') { }
if (Array.isArray(value)) { }Import Organization
// 1. External packages
import { useState } from 'react';
import { z } from 'zod';
// 2. Internal modules (use @/ alias)
import { Button } from '@/components/ui/Button';
import { prisma } from '@/lib/prisma/prisma';
// 3. Types
import type { IUser } from '@/types/UserInterfaces';
// 4. Styles
import styles from './Component.module.css';Prefer named exports over default exports (except Next.js pages/layouts).
Component Structure
'use client'; // Only if needed
// 1. Imports
import { useState } from 'react';
import { Button } from '@/components/ui/Button';
// 2. Interface
interface IComponentProps {
title: string;
isActive?: boolean;
}
// 3. Component
export function ComponentName({ title, isActive = false }: IComponentProps) {
// 4. Hooks (always at top)
const [state, setState] = useState(false);
// 5. Event handlers
const handleClick = () => {
setState(true);
};
// 6. Early returns
if (!title) return null;
// 7. JSX
return (
<div>
<h1>{title}</h1>
<Button onClick={handleClick}>Click</Button>
</div>
);
}Server vs Client Components
// Server Component (default) - no 'use client'
export async function BlogList() {
const posts = await prisma.post.findMany();
return <div>{posts.map(post => ...)}</div>;
}
// Client Component - add 'use client'
'use client';
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}Use Client Components for: hooks, event handlers, browser APIs, third-party libraries.
Error Handling
export async function POST(req: Request) {
try {
const body = await req.json();
const validated = schema.parse(body);
const result = await processData(validated);
return NextResponse.json({ success: true, data: result });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: 'Validation failed', details: error.errors },
{ status: 400 }
);
}
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}Async/Await
// Prefer async/await
async function fetchData(userId: string) {
const user = await prisma.user.findUnique({ where: { id: userId } });
const orders = await prisma.order.findMany({ where: { userId } });
return { user, orders };
}
// Use Promise.all() for parallel operations
async function fetchDashboard() {
const [users, orders, products] = await Promise.all([
prisma.user.findMany(),
prisma.order.findMany(),
prisma.product.findMany()
]);
return { users, orders, products };
}Best Practices
Avoid magic numbers
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
if (file.size > MAX_FILE_SIZE) { }Single responsibility
// Each function does one thing
function validateEmail(email: string): boolean { }
function sendWelcomeEmail(email: string): Promise<void> { }DRY (Don't Repeat Yourself)
function formatCurrency(amount: number): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(amount);
}Run npm run lint and npm run prettier before committing. Husky hooks enforce this automatically.
How is this guide ?
Last updated on