Skip to content

Frontend Coding Standards

All frontend projects developed at DIT must adhere to consistent coding standards that ensure code quality, maintainability, and team collaboration. These standards apply to all frontend applications regardless of the chosen framework or build tool.

TypeScript Requirement

TypeScript is mandatory for all frontend projects.

All new frontend projects must be written in TypeScript (version >= 5). JavaScript-only projects are not permitted unless explicitly authorized by the Head of Digital Development.

TypeScript Standards

  • Strict Mode: All projects must enable TypeScript's strict mode in tsconfig.json:

    json
    {
      "compilerOptions": {
        "strict": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "strictFunctionTypes": true,
        "strictBindCallApply": true,
        "strictPropertyInitialization": true,
        "noImplicitThis": true,
        "alwaysStrict": true
      }
    }
  • Type Safety: Avoid using any type. Use unknown when the type is truly unknown, and use proper type guards to narrow types.

  • Type Definitions: All public APIs, function parameters, and return types must be explicitly typed. Avoid relying on type inference for public interfaces.

  • Type Imports: Use type-only imports when importing types:

    typescript
    import type { User } from './types';
    import { fetchUser } from './api';

ESLint & Prettier Enforcement

All frontend projects must use ESLint and Prettier for code quality and formatting.

ESLint Configuration

  • ESLint must be configured and enforced in all frontend projects.

  • ESLint configuration must extend from established presets appropriate for the framework:

    • React projects: Use eslint-config-react-app or @typescript-eslint/recommended with React-specific rules
    • Vue projects: Use @vue/eslint-config-typescript or plugin:vue/recommended
    • Next.js projects: Use next/core-web-vitals and next/typescript
  • ESLint must be integrated into the development workflow:

    • ESLint must run as part of the pre-commit hooks
    • ESLint errors must block CI/CD pipeline execution
    • ESLint warnings should be addressed, but may not block deployment if explicitly documented

Prettier Configuration

  • Prettier must be configured for consistent code formatting across all projects.

  • A .prettierrc or prettier.config.js file must be present in the project root.

  • Recommended Prettier configuration:

    json
    {
      "semi": true,
      "trailingComma": "es5",
      "singleQuote": true,
      "printWidth": 120,
      "tabWidth": 4,
      "useTabs": false,
      "arrowParens": "avoid"
    }
  • Prettier must be integrated with ESLint using eslint-config-prettier to avoid conflicts.

  • Prettier must run automatically on save (via editor configuration) and as part of pre-commit hooks.

Editor Integration

  • All team members must configure their editors to:
    • Format on save using Prettier
    • Show ESLint errors and warnings in real-time
    • Auto-fix ESLint issues where possible

Component-Based Architecture

All frontend projects must follow a component-based architecture.

Framework Requirements

Frontend projects must use one of the approved component-based frameworks:

  • React (version >= 17) with TypeScript
  • Vue (version >= 3) with TypeScript
  • Next.js (version >= 15) with TypeScript and React

Component Design Principles

  • Single Responsibility: Each component should have a single, well-defined purpose.
  • Reusability: Components should be designed for reuse across the application.
  • Composition: Prefer composition over inheritance. Build complex components by composing simpler ones.
  • Props Interface: All component props must be explicitly typed using TypeScript interfaces or types.
  • Component Size: Components should be kept small and focused. If a component exceeds 200-300 lines, consider breaking it into smaller components.

React-Specific Standards

  • Use functional components with hooks. Class components are not permitted in new code.

  • Use React.FC or explicit return types for component functions:

    typescript
    interface ButtonProps {
      label: string;
      onClick: () => void;
    }
    
    const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
      return <button onClick={onClick}>{label}</button>;
    };
  • Custom hooks must be prefixed with use (e.g., useAuth, useApi).

  • Use proper dependency arrays in useEffect, useMemo, and useCallback.

Vue-Specific Standards

  • Use Composition API with <script setup> syntax for new components.

  • Define props using defineProps<T>() with TypeScript:

    typescript
    <script setup lang="ts">
    interface Props {
      title: string;
      count?: number;
    }
    
    const props = defineProps<Props>();
    </script>
  • Use defineEmits<T>() for type-safe event emissions.

  • Prefer ref and reactive from Composition API over Options API.

Directory Structure Standards

All frontend projects must follow a consistent directory structure.

Standard Directory Layout

All frontend projects must organize code using the following directory structure:

src/
├── components/          # Reusable UI components
│   ├── common/         # Shared/common components (Button, Input, etc.)
│   ├── layout/         # Layout components (Header, Footer, Sidebar)
│   └── feature/        # Feature-specific components
├── hooks/              # Custom React hooks (React projects)
│   └── composables/    # Vue composables (Vue projects)
├── pages/              # Page components/routes
│   └── [feature]/      # Feature-specific pages
├── services/           # API services and external integrations
│   ├── api/            # API client functions
│   └── utils/          # Utility functions
├── stores/             # State management (Redux, Zustand, etc.)
├── types/              # TypeScript type definitions
├── constants/          # Application constants
├── assets/             # Static assets (images, fonts, etc.)
│   ├── images/
│   └── styles/
├── styles/             # Global styles and themes
└── __tests__/          # Test files (or co-located with components)

Directory Naming Conventions

  • Use lowercase with hyphens for directory names: user-profile/, api-client/
  • Use PascalCase or lowercase with hyphens for component files: UserProfile.tsx, Button.tsx (or user-profile.tsx, button.tsx)
  • Use UPPERCASE for constant files: API_ENDPOINTS.ts, ROUTES.ts

File Organization Rules

  • One component per file: Each component should be in its own file.

  • Index files: Use index.ts or index.tsx files to export components from directories:

    typescript
    // components/common/index.ts
    export { Button } from './Button';
    export { Input } from './Input';
  • Co-location: Keep related files together:

    components/
    └── UserCard/
        ├── UserCard.tsx
        ├── UserCard.test.tsx
        ├── UserCard.styles.ts
        └── index.ts
  • Barrel exports: Use index files to create clean import paths:

    typescript
    // Instead of: import { Button } from './components/common/Button'
    import { Button } from './components/common';

Service Layer Organization

  • API services should be organized by domain or feature:

    services/
    ├── api/
    │   ├── auth.ts        # Authentication API calls
    │   ├── users.ts       # User-related API calls
    │   └── products.ts    # Product-related API calls
    └── utils/
        ├── httpClient.ts  # HTTP client configuration
        └── errorHandler.ts
  • Each service file should export typed functions:

    typescript
    // services/api/users.ts
    import type { User } from '@/types';
    
    export const fetchUser = async (id: string): Promise<User> => {
      // Implementation
    };

Code Quality Requirements

Naming Conventions

  • Components: PascalCase or lowercase with hyphens (UserProfile, NavigationBar, user-profile, navigation-bar)
  • Functions/Variables: camelCase (getUserData, isLoading)
  • Constants: UPPER_SNAKE_CASE (API_BASE_URL, MAX_RETRIES)
  • Types/Interfaces: PascalCase (User, ApiResponse)

Import Organization

  • Group imports in the following order:
    1. External dependencies (React, Vue, libraries)
    2. Internal absolute imports (using @/ alias)
    3. Relative imports
    4. Type imports (using import type)

Example:

typescript
import React, { useState, useEffect } from 'react';
import { Button } from '@/components/common';
import { fetchUser } from '@/services/api/users';
import type { User } from '@/types';

Path Aliases

  • Configure path aliases in tsconfig.json and build tool configuration:

    json
    {
      "compilerOptions": {
        "paths": {
          "@/*": ["./src/*"],
          "@/components/*": ["./src/components/*"],
          "@/services/*": ["./src/services/*"]
        }
      }
    }
  • Use aliases instead of relative paths for cleaner imports:

    typescript
    // Good
    import { Button } from '@/components/common';
    
    // Avoid
    import { Button } from '../../../components/common';

Security Requirements for Frontend

All frontend applications must implement security best practices to protect against common vulnerabilities and ensure secure data handling.

No Sensitive Logic or Secrets in Frontend Code

  • Never include sensitive logic, API keys, secrets, or credentials in frontend code.
  • All sensitive operations must be performed server-side.
  • Frontend code is publicly accessible and can be inspected by anyone.
  • Environment variables used in frontend builds are embedded in the bundle and are not secure for sensitive data.

Prohibited:

  • API keys or secrets in frontend code
  • Authentication tokens or passwords in client-side code
  • Business logic that should be server-side (e.g., payment processing, data validation that affects security)
  • Database connection strings or credentials

Required:

  • All sensitive operations must be handled through secure backend APIs
  • Use environment variables only for non-sensitive configuration (e.g., public API endpoints, feature flags)

HTTPS-Only Communication

  • All frontend applications must use HTTPS exclusively in production.
  • HTTP traffic must redirect to HTTPS.
  • All API calls must use HTTPS endpoints.
  • Mixed content (HTTP resources on HTTPS pages) is prohibited.

Implementation:

  • Configure web servers to redirect HTTP to HTTPS
  • Use Strict-Transport-Security (HSTS) headers
  • Ensure all external resources (CDNs, APIs) use HTTPS
  • Test that HTTP redirects work correctly

Cross-Site Scripting (XSS) Protection

XSS attacks are a critical security risk and must be prevented through multiple layers of defense.

Input Sanitization

  • All user input must be sanitized before rendering.
  • Never render user-provided content directly without sanitization.
  • Use framework-specific sanitization methods:
    • React: Use React's built-in escaping (automatic for JSX), but sanitize HTML content if using dangerouslySetInnerHTML
    • Vue: Use Vue's built-in escaping, sanitize HTML if using v-html
    • Use libraries like DOMPurify for HTML sanitization when needed

Example:

typescript
// Good - React automatically escapes
const UserDisplay = ({ name }: { name: string }) => {
  return <div>{name}</div>; // Safe - React escapes automatically
};

// Dangerous - requires sanitization
const UserContent = ({ htmlContent }: { htmlContent: string }) => {
  const sanitized = DOMPurify.sanitize(htmlContent);
  return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
};

Additional XSS Prevention

  • Avoid eval() and Function() constructors
  • Be cautious with innerHTML, outerHTML, and similar DOM manipulation
  • Validate and sanitize data from URLs (query parameters, hash fragments)
  • Use textContent instead of innerHTML when possible

Cross-Site Request Forgery (CSRF) Protection

  • All state-changing operations must be protected against CSRF attacks.
  • CSRF protection must be implemented through backend tokens, not frontend-only solutions.

Required Implementation:

  • Backend Token-Based Protection:

    • Backend must generate and validate CSRF tokens
    • Tokens must be included in all state-changing requests (POST, PUT, DELETE, PATCH)
    • Tokens should be unique per session and rotated regularly
  • Frontend Responsibilities:

    • Include CSRF tokens in request headers or request bodies as required by backend
    • Never expose CSRF tokens in URLs or logs
    • Ensure tokens are sent with all state-changing API calls

Example:

typescript
// Include CSRF token in API requests
const apiClient = axios.create({
  headers: {
    'X-CSRF-Token': getCsrfToken(), // Retrieved from backend endpoint
  },
});
  • SameSite Cookie Attribute: Backend should set cookies with SameSite=Strict or SameSite=Lax to provide additional CSRF protection

Secure Storage Practices

  • Never store sensitive data in localStorage, sessionStorage, or cookies accessible to JavaScript.

Prohibited Storage:

  • Authentication tokens (JWT, access tokens, refresh tokens)
  • Passwords or password hashes
  • Credit card numbers or payment information
  • Personal Identifiable Information (PII) that should be protected
  • API keys or secrets

Secure Storage Guidelines:

  • localStorage/sessionStorage: Only store non-sensitive data:

    • User preferences (theme, language)
    • UI state (collapsed panels, filters)
    • Non-sensitive feature flags
    • Public configuration data
  • Cookies: Sensitive data in cookies must be:

    • Set with HttpOnly flag (not accessible to JavaScript)
    • Set with Secure flag (HTTPS only)
    • Set with SameSite attribute (Strict or Lax)
  • Session Management:

    • Use secure, HttpOnly cookies for session identifiers
    • Implement proper session expiration and invalidation
    • Never store session data in localStorage

Example:

typescript
// ❌ Bad - Storing sensitive data
localStorage.setItem('authToken', token);
localStorage.setItem('userPassword', password);

// ✅ Good - Storing non-sensitive preferences
localStorage.setItem('theme', 'dark');
localStorage.setItem('language', 'en');

// ✅ Good - Sensitive data handled server-side
// Session stored in HttpOnly cookie, tokens never reach frontend

Additional Security Best Practices

  • Dependency Management:

    • Regularly update dependencies to patch security vulnerabilities
    • Use tools like npm audit or pnpm audit to check for known vulnerabilities
    • Review and approve dependency updates before merging
  • Error Handling:

    • Never expose sensitive information in error messages
    • Avoid revealing system internals (stack traces, file paths) in production
    • Log errors server-side, not client-side
  • Authentication:

    • Implement proper logout functionality that invalidates sessions
    • Use secure authentication flows (e.g., OAuth 2.0, OpenID Connect)
    • Never store authentication credentials in frontend code
  • Data Validation:

    • Validate all user input on both client and server side
    • Client-side validation improves UX but never replaces server-side validation
    • Use TypeScript types to enforce data structure validation

Enforcement and Compliance

  • Pre-commit Hooks: All projects must use pre-commit hooks to enforce:

    • ESLint checks
    • Prettier formatting
    • TypeScript type checking
  • CI/CD Integration: The CI/CD pipeline must:

    • ESLint Enforcement:
      • Run ESLint on all code changes
      • ESLint errors must block CI/CD pipeline execution and prevent merging
      • ESLint warnings should be addressed, but may not block deployment if explicitly documented and approved
      • ESLint must run as part of the build/test pipeline before deployment
    • Run TypeScript compiler checks (tsc --noEmit) and fail on type errors
    • Verify code formatting with Prettier and fail if code is not properly formatted
  • Code Reviews: All pull requests must be reviewed for compliance with these standards before merging.

  • Exceptions: Any deviation from these standards requires:

    1. Clear justification documented in the pull request
    2. Approval from the Head of Digital Development

By adhering to these coding standards, DIT ensures that all codebases are maintainable, scalable, and consistent across teams and projects.