We use tracking cookies to understand how you use the product and help us improve it. For more information on how we store cookies, read our  privacy policy.

Code Style

TypeScript patterns and component structure

Modern TypeScript and React patterns used in Plainform.

TypeScript

Type Annotations

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 inferred

Interfaces vs Types

Interfaces 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

Type guards
function isError(value: unknown): value is Error {
  return value instanceof Error;
}

if (typeof value === 'string') { }
if (Array.isArray(value)) { }

Import Organization

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

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

Error handling in API routes
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

Async/await patterns
// 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

Use constants
const MAX_FILE_SIZE = 5 * 1024 * 1024;  // 5MB
if (file.size > MAX_FILE_SIZE) { }

Single responsibility

Single responsibility
// Each function does one thing
function validateEmail(email: string): boolean { }
function sendWelcomeEmail(email: string): Promise<void> { }

DRY (Don't Repeat Yourself)

DRY principle
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