Skip to content

Web Frontend Development Guidelines

Version History

Version Date Description/Updates
1.0 April 2026 Publish to greater DHI community
0.3 Feb 2026 Updates after second stakeholder review
0.2 Nov 2025 Updates after first stakeholder review
0.1 Aug 2025 Initial Content

Introduction

Defines the goals and scope of these guidelines so teams understand when and how to apply them. Provides a common standard for DHI web front-end development to improve consistency, maintainability, and collaboration while still allowing room for innovation.

Contacts

Roles to Apply

Development roles:

  • Architects
  • Frontend developers
  • Product Manager/Product Owners

Enforcement Levels

For terminology see Guideline Enforcement Levels

Section Enforcement Level
Core Stack Overview Expected
Principles and Exceptions Important
Development Environment Important
Code and Architecture Important
State Management Expected
Architecture Approaches Expected
Recommended Packages DHI good practice
Testing Important
Accessibility (a11y) Important
Performance Important
Security Mandatory
Code Reviews Important

References

Core Stack Overview

Defines the baseline technologies used across DHI web projects for consistency and reuse. Standardizing on a core stack enables code sharing, simplifies hiring, and lets developers move between projects without re-learning fundamentals.

Application Types

  • Prefer SPA (Single Page Application) architecture with client-side routing
  • Avoid SSR frameworks (for example Next.js) unless an approved exception exists
  • For mobile applications use React Native

Technologies

Area Default Notes
UI framework React Declarative components, strong ecosystem, widely adopted at DHI
Language TypeScript Static typing, safer refactoring, strong IDE support
State management MobX or Zustand MobX: Reactive state, minimal boilerplate, supports complex interconnected state.
Zustand: Lightweight, unopinionated state store with a simple hook‑based API.
Styling CSS-in-JS (Emotion / Styled Components) Co-located styles, scoped automatically, dynamic styling

Principles and Exceptions

Defines how to balance consistency with pragmatic exceptions across projects. Shared principles make it clearer when to stick to defaults and when a justified exception is appropriate, avoiding both rigid conformity and chaotic inconsistency.

General Principles

  • Baseline: All applications use React + MUI + TypeScript by default
  • Consistency first: Reduces onboarding time and enables cross-team collaboration
  • Pragmatism over rigidity: Innovation is encouraged when requirements demand it

Acceptable Exceptions

  • Offline-first / low-bandwidth: Service workers, IndexedDB, server components
  • High-performance: WebGL/Canvas-based visualizations, heavy dataflow
  • Lightweight: Landing pages where React/MUI add unnecessary overhead

Exception Process

  1. Document the reason for deviation
  2. Propose alternative with trade-offs
  3. If the deviation is for a single component, prototype project, or single client project with a handful of users then discuss with at least one other senior developer as a sanity check. If this is a high impact project with a high budget, high strategic value, or large user base then contact the architecture board for review.
  4. Ensure alignment with accessibility, security, and maintainability standards

Documentation & Knowledge Sharing

Defines a standard README structure so developers can quickly orient themselves in any DHI project. Good documentation also reduces onboarding time, prevents knowledge silos, and keeps projects maintainable as team members change.

Every project README.md should include:

Section Contents
Identity Project name, CI status badge, purpose summary
Metadata Maconomy ID, department, client, key roles (PO, PM, Dev Lead)
Tech Stack Node version, critical libraries, auth method (IAMv2, OAuth). Note any deviations from baseline
Environments External APIs consumed, DEV/PROD deployment links, local .env configuration
Design Link to Figma/Miro source of truth

Development Environment

Defines the recommended local tooling and setup to reduce "works on my machine" issues. Consistent environments reduce setup friction for new developers and minimise environment-related bugs.

Environment Isolation

Tool Use Case
PNPM Node.js version management via pnpm env (see Dependency Management)
NVM Manage multiple Node.js versions per project
VS Code Dev Containers Project-specific environment with pre-installed dependencies

Code Editor

Item Recommendation
Editor VS Code (recommended)
Extensions ESLint, GitLens, Error Lens

Monorepo vs Standalone

Option When to use Notes
Standalone (default) Most projects; different teams/budgets or limited shared code Separate repository per project
Monorepo Shared libraries or tightly related applications Use PNPM workspaces with Turborepo

Note: NX is losing community support so it has been removed as a recommendation

Dependency Management

Defines the recommended package manager and supporting practices.

Package managers

Manager When to use Why
PNPM (recommended) All new projects and shared workspaces Disk-efficient centralized store, strict dependency resolution, built-in Node.js version management via pnpm env
NPM Legacy projects or where PNPM is not yet adopted Standard, bundled with Node.js

Corepack

  • Use Corepack to ensure the correct package manager version per project based on the packageManager field in package.json.

Practices

  • Commit lock files (pnpm-lock.yaml, package-lock.json) to ensure reproducible builds
  • Lock versions explicitly — avoid floating ranges (^, latest, *, 1.x.x) for production dependencies until an intentional upgrade
  • Separate devDependencies from production dependencies to reduce bundle size
  • Run regular auditspnpm audit or npm audit to check for vulnerabilities

Build Tools

Tool Purpose
Vite (recommended) Fast dev server with HMR and optimized production builds

Useful Plugins

Plugin When to use Why
observing-components Using MobX stores with React components that should react to observable state Removes manual observer() wrappers by wrapping components at build time via Babel/SWC, reducing missed components and boilerplate

Browser Targets

Scope Recommendation
Default Support the latest two versions of Chrome
Per project Define exact browser support (Chrome, Edge, Firefox, Safari) in the project README or documentation

Code and Architecture

Defines conventions for structure, layering, and style so codebases stay predictable, navigable, and easy to review.

File and Folder Structure

  • Workspace imports: Use absolute imports via ~/ or@/, use relative import within local folder (whichever path is smaller)
  • Configure this in tsconfig.json and vite.config.ts
  • Naming:
  • Prefer to name files to mirror the name of the main exported symbol
    • SomeComponent.tsx exports SomeComponent
    • useRootStore.ts exports useRootStore
  • For collections of functionality:
    • index.ts as a barrel file
    • theming.ts for theme related exports
    • mapUtils.ts for map-related utility functions
  • Exports:
  • Avoid default exports
    • Named exports are explicit and make refactoring easy and it also avoids duplication of symbols

Principles

Principle Guideline
SRP One responsibility per module/component
DRY Avoid repetition; don't over-abstract prematurely
ISP Keep props and APIs small and focused
Size Aim for ~300 lines per file; extract hooks/subcomponents as needed

Separate business logic (stores, hooks) from presentation (components).

Coding Style

General

  • Functional programming style only (no class-based React components, .map() over for loops & impertive code)
  • Explicitly type all parameters and important return types

Styling

  • Prefer CSS-in-JS using Emotion for co-located, theme-aware styles
  • Use Emotion's css prop pattern for small, component-local styles where it keeps JSX readable
  • Tailwind CSS is acceptable for utility-first styling where it clearly benefits the project and the team agrees on its use
  • Avoid inline style for anything non-trivial; centralize styles in CSS-in-JS or Tailwind utilities

Commenting

  • English only; comments explain why, not what
  • Document: domain constraints, performance trade-offs, third-party workarounds
  • TODO/FIXME allowed with ticket links; no commented-out code in main branches

State Management

Defines how to handle global, local, and cross-cutting state consistently across applications.

Type Solution
Global / client state MobX (recommended) or Zustand / Jotai
Local UI state useState / useReducer for trivial, short-lived state only
Cross-cutting concerns Context API (theme, auth, feature flags) — keep small and focused

Architecture Approaches

Defines the primary architectural models for structuring state and data flow; choose one main model per project. Avoid mixing patterns that duplicate responsibility or create multiple sources of truth.

✅ Preferred: State-centric (layered / store-driven)

Recommended for medium–large and long-lived applications.

  • Stores/services own data fetching, caching, transformations, and business logic
  • For server-side data, prefer fetch wrapped in services or integrate TanStack Query via mobx-tanstack-query inside stores (not in components)
  • Derived/computed state lives in the store layer
  • Components focus on rendering and user interaction only
  • Single source of truth for application state
  • Easier debugging and reasoning about data flow
  • Fewer sync issues and less glue code
  • Proven to scale better for complex apps and larger teams

Derived State

The principle behind derived state is single source of truth: any piece of data should have exactly one authoritative location. When you store data that can be calculated from other state, you create multiple sources of truth that inevitably drift out of sync. Derived state eliminates this category of bugs entirely by computing values on demand rather than storing them.

Why it matters:

  • Eliminates sync bugs — Redundant state becomes stale when source data changes; derived state is always current
  • Reduces complexity — Fewer state updates to coordinate, less code to maintain
  • Improves reliability — No possibility of forgetting to update a cached copy

Example: Each layer derives from the previous; only raw API data is stored:

  • Stored: stationData?: StationDto[] — raw JSON from API response
  • Derived: get stations() — transforms raw data into domain models: this.stationData.map(s => new Station(s))
  • Derived: get activeStations() — filters domain models: this.stations.filter(s => s.id === this.activeStationId)

This means that fetching new stationData or updating activeStationId will cascade through the layers automatically.

MobX get accessors handle memoization and reactivity automatically.


Alternative: Component-centric (colocated/server hooks)

Suitable for small–medium apps or when not using a global store.

  • Components own data fetching and caching (e.g., TanStack Query / React Query hooks)
  • State is colocated with the UI that consumes it
  • Minimal or no global state layer
  • Faster to scaffold simple features
  • Less upfront structure
  • Logic can become scattered and duplicated as apps grow
  • Harder to share derived data or coordinate behavior across features

UX Design

Defines expectations for collaborating with designers and delivering responsive, user-friendly interfaces. Clear collaboration and shared responsive design expectations help prevent miscommunication and costly rework.

Design Workflow

  • Receive annotated designs from Figma/Miro; kickoff meeting recommended
  • DHI designers may have UX or engineering backgrounds — adjust communication accordingly

Responsive Design

  • Default target: desktop; define mobile targets per project
  • Use MUI breakpoints or CSS media queries
  • Test with browser DevTools; consider touch interactions for mobile

Defines default libraries for common frontend needs so teams benefit from shared experience and compatible choices. Standardizing on proven packages reduces decision fatigue and helps teams build collective expertise.

UI Component Library

Library Best For
MUI Comprehensive components, consistent design language, accessibility built-in, strong theming
Ant Design Enterprise applications, rich data display components (tables, forms), more ergonomic component API

MUI is the default choice. Ant Design is a solid more performant/simpler alternative, especially for data-heavy admin interfaces.

Charting

Feature ECharts Plotly Highcharts
Pros High performance, rich chart types, flexible config Interactive, scientific charts, good docs Mature, excellent docs, accessibility
Cons Smaller community, less intuitive API Larger bundle, limited styling flexibility Commercial license required
React ✅ echarts-for-react ✅ react-plotly.js ✅ highcharts-react-official
TypeScript ⚠️ Partial (DefinitelyTyped) ✅ Native types ⚠️ Partial (improving)
Performance ✅ Excellent ⚠️ Moderate ✅ Good
Licensing ✅ Free (Apache 2.0) ✅ Free (MIT) ⚠️ Commercial

Summary: ECharts for high-performance/config-heavy apps; Plotly for scientific/interactive visualizations; Highcharts for enterprise with strong docs (licensing cost).

Mapping

Feature Mapbox GL JS/MapLibre OpenLayers Leaflet
Pros Beautiful vector maps, WebGL performance, rich styling Powerful GIS operations, flexible Lightweight, large plugin ecosystem
Cons Token required (usage limits), commercial for advanced use Steep learning curve, verbose API Limited vector/WebGL support
React ✅ react-map-gl ⚠️ Community wrappers ✅ react-leaflet
TypeScript ✅ Native types ✅ Excellent ⚠️ Partial
Performance ✅ Excellent (WebGL) ✅ Good for GIS ⚠️ Moderate
Licensing ⚠️ Free tier with limits; Mapbox commercial / MapLibre free (BSD) ✅ Free (BSD) ✅ Free (BSD)

deck.gl integrates as high-performance WebGL layers within Mapbox/MapLibre for large-scale data visualization.

Forms

Approach Best For
MobX + MUI/AntD Complex interdependent fields, reactive validation
React Hook Form + Zod Performance-critical, schema-based validation

Provide clear validation messages; field-level validation for immediate feedback.

Network Requests

Library Purpose
fetch (built-in) Standard browser HTTP client; wrap in small helpers/services for base URL, headers, and error handling
TanStack Query (React Query) Server state, caching, background refetch via hooks
tRPC Type-safe end-to-end RPC between TypeScript backend and frontend; eliminates manual REST types and reduces client/server drift
mobx-tanstack-query Integrates TanStack Query caching/invalidations into MobX stores in state-centric apps

API specs: Generate TypeScript types from OpenAPI (orval, openapi-typescript) or GraphQL (graphql-codegen). APIs under DHI control should provide specs.

Internationalization (I18n)

react‑i18next — i18next-based internationalization with hooks, namespaced translation files, interpolation, and runtime language switching.

Use descriptive message IDs (dashboard.header.title); format dates/numbers with locale-aware formatters.


Testing

Defines the recommended testing tools and strategy so teams can refactor safely and catch regressions early. Automated tests also act as living documentation of expected behavior and help control long-term maintenance costs.

Tool Purpose
Vitest Unit and integration tests
Playwright E2E testing
Storybook Component documentation and visual testing
MSW API mocking
  • Co-locate unit tests: utils.tsutils.test.ts
  • Focus on user behavior, not implementation details
  • Unit testing components is largley unecessary on the frontend
  • Prefer to focus on stories, Storybook snapshots, integration tests and E2E tests

Accessibility (a11y)

Defines baseline accessibility expectations so applications work for all users and meet legal requirements. Good accessibility also improves overall usability through clearer structure, better contrast, and keyboard-friendly interactions.

  • Semantic HTML (<button>, <nav>, <main>)
  • Keyboard navigation for all interactive elements
  • WCAG 2.1 AA color contrast (4.5:1 normal, 3:1 large text)
  • Use eslint-plugin-jsx-a11y for static analysis
  • MUI has built-in a11y; add labels for form controls and icon-only buttons

Performance

Defines performance targets and strategies to keep applications fast and responsive in production. Proactive performance work is far cheaper than fixing user-facing issues after launch and directly affects business outcomes.

Targets: LCP < 2.5s, FID < 100ms, CLS < 0.1

Area Strategies
Bundle Code splitting (React.lazy), tree-shaking, bundle analysis
Rendering Virtualization (react-window)
Data Efficient server-state management (e.g., TanStack Query in component-centric apps, or well-structured MobX stores in state-centric apps); avoid unnecessary refetches
Assets WebP images, CDN, gzip/brotli compression

Error Handling

Defines patterns for capturing, reporting, and surfacing errors so failures are visible and user impact is minimized. Robust error handling prevents cascading failures and turns potential crises into manageable incidents.

  • Error boundaries: Wrap feature areas to catch rendering errors
  • API errors: Handle at service layer; show user-friendly messages
  • Monitoring: Use Sentry for production error tracking with context (user, route, state)
  • Logging: No sensitive data; guard console statements in production

Security

Defines baseline security practices to protect data, reduce risk, and bake security into everyday development. Addressing security early avoids leaks, reputational damage, and expensive retrofits later.

Concern Prevention
XSS Avoid dangerouslySetInnerHTML; use DOMPurify
CSRF CSRF tokens, SameSite cookies
Secrets Never in client code; use env variables
Dependencies Regular npm audit / pnpm audit

Auth: Use IAMv2/OAuth 2.0/OIDC. Prefer httpOnly cookies over localStorage. Server-side authorization is mandatory; client-side is UX only.


Code Reviews

Defines expectations for review scope, checklist use, and feedback so changes stay aligned with team standards. Reviews catch bugs, spread knowledge, and are an investment in long-term maintainability and team growth.

Checklist

  • [ ] Follows conventions
  • [ ] Components sized appropriately
  • [ ] Business/presentation logic separated
  • [ ] Error cases handled
  • [ ] Tests included for new or alterned functionality
  • [ ] No security issues
  • [ ] Performance considered

Process: PRs should be small, focused on single feature per PR, clear descriptions, constructive feedback.


Provides curated references for patterns, tooling, and DHI resources worth revisiting over time. These links offer deeper dives into patterns, best practices, and DHI-specific tooling.

React Patterns & Architecture:

  • https://www.patterns.dev/react/ — Modern React patterns
  • https://refactoring.guru/design-patterns — General design patterns

Learning Resources:

  • https://www.youtube.com/@cosdensolutions — React tutorials

DHI Resources:

  • https://github.com/DHI — DHI public repositories
  • https://github.com/DHI-GRAS/ — DHI GRAS repositories
  • https://www.npmjs.com/package/@dhi-gras/builder — DHI GRAS builder package