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
anytype. Useunknownwhen 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:
typescriptimport 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-appor@typescript-eslint/recommendedwith React-specific rules - Vue projects: Use
@vue/eslint-config-typescriptorplugin:vue/recommended - Next.js projects: Use
next/core-web-vitalsandnext/typescript
- React projects: Use
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
.prettierrcorprettier.config.jsfile 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-prettierto 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.FCor explicit return types for component functions:typescriptinterface 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, anduseCallback.
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
refandreactivefrom 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(oruser-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.tsorindex.tsxfiles 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.tsBarrel 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.tsEach 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:
- External dependencies (React, Vue, libraries)
- Internal absolute imports (using
@/alias) - Relative imports
- Type imports (using
import type)
Example:
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.jsonand 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
DOMPurifyfor HTML sanitization when needed
- React: Use React's built-in escaping (automatic for JSX), but sanitize HTML content if using
Example:
// 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()andFunction()constructors - Be cautious with
innerHTML,outerHTML, and similar DOM manipulation - Validate and sanitize data from URLs (query parameters, hash fragments)
- Use
textContentinstead ofinnerHTMLwhen 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:
// 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=StrictorSameSite=Laxto 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
HttpOnlyflag (not accessible to JavaScript) - Set with
Secureflag (HTTPS only) - Set with
SameSiteattribute (StrictorLax)
- Set with
Session Management:
- Use secure, HttpOnly cookies for session identifiers
- Implement proper session expiration and invalidation
- Never store session data in localStorage
Example:
// ❌ 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 frontendAdditional Security Best Practices
Dependency Management:
- Regularly update dependencies to patch security vulnerabilities
- Use tools like
npm auditorpnpm auditto 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
- ESLint Enforcement:
Code Reviews: All pull requests must be reviewed for compliance with these standards before merging.
Exceptions: Any deviation from these standards requires:
- Clear justification documented in the pull request
- 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.
