Skip to main content

Frontend Style Guide

This section contains the code style guide for the Civitas Core V2 frontend code. This style guide should be respected at any point and also be considered in code reviews to achieve a clean, consistent codebase with a good readability.

This style guide is not immutable. If we recognise that some rules don't make sense or can be improved, we can change them in consultation with the team. This shouldn't happen too often and at best only in the beginning (the later we change a rule, the more code has to be adjusted…).

The ESLint configuration which implements these guidelines can be found here: https://gitlab.com/civitas-connect/civitas-core/civitas-core-v2/civitas-core-platform/-/blob/main/portal-frontend/eslint.config.mjs

Naming conventions

  • Variables, functions, instances: camelCase
  • Constants: SCREAMING_SNAKE_CASE
  • React components: PascalCase
  • Classes, constructors: PascalCase
  • Props: camelCase for prop names, or PascalCase if the prop value is a React component
  • Acronyms, initialisms: all uppercased, or all lowercased
  • booleans: start with a prefix (is, has, should...)
  • Folder names: kebab-case
  • Files containing Classes or ReactComponents: PascalCase
  • Files containing helper functions (utils): camelCase

Use let and const instead of var

Quotes

Use double quotes (") for JSX attributes, but single quotes (') for all other JS

Don't use semicolons

Don't end statements with a semicolon

Max line length: 120

Use template strings for string concatenation

const world = "world";

❌ "hello " + world
❌ "hello ".concat(world)
✅ `hello ${world}`

No magic numbers

Numbers should be declared as constants to make their meaning explicit


const DUTY_FREE_PRICE = 100;
const FINAL_PRICE = DUTY_FREE_PRICE + (DUTY_FREE_PRICE \* 0.2);


const DUTY_FREE_PRICE = 100;
const TAX = 0.2;
const FINAL_PRICE = DUTY_FREE_PRICE + (DUTY_FREE_PRICE \* TAX);

Imports / Exports

  • sort imports and exports alphabetically
  • Don't use the following file extensions in imports: .js, .jsx, .ts, .tsx

No async code in constructor

Never do async operations inside a constructor!

Prefer arrow functions

  • use arrow functions instead of function declarations, except when they are necessary

No chaining of array methods

❌ ["a", "b", "c"].filter(...).map(...)


const filteredArray = ["a", "b", "c"].filter(...);
const mappedArray = filteredArray.map(...)

No promise chains

  • Use Async / Await

Prefer positive conditions

❌ if (!hasAccess) { ... }

✅ if (hasAccess) { ... }
  • clearer, easier to read
  • exceptions: when the negative case is the main concern if (!items.length) return;, for early returns to avoid nesting

Ternary Operator

  • Use it for simple operations only (One condition)
  • No chaining or nesting
  • Otherwise use if-else-variations
  • The example below shows the obvious reason: readability / maintainability

isDeletable
? 'Delete'
: usecase.deployment_status == DeploymentState.Failed
? 'Assignments can not be deleted when deployment has an error'
: usecase?.deployment_status == DeploymentState.InProgress ||
usecase?.deployment_status == DeploymentState.Initiated
? 'Assignments can not be deleted when deployment is in progress'
: 'Assignments can not be deleted while the assignment process is running.'


if (isDeletable) {
return 'Delete';
}
switch (usecase?.deployment_status) {
case DeploymentState.Failed: {
return 'Assignments can not be deleted when deployment has an error';
}
case DeploymentState.InProgress: {
return 'Assignments can not be deleted when deployment is in progress';
}
case DeploymentState.Initiated: {
return 'Assignments can not be deleted while the assignment process is running.';
}
}
return undefined;

Only one React component per file

  • multiple Stateless, or Pure, Components are allowed

Prefer interfaces for component props

  • define an interface for component props to keep them clear, reusable, and extendable
  • don't use inline typing

const MyComponent = ({
children, theme, lang
}: Readonly<{
interface MyComponentProps {
children: React.ReactNode, theme: Theme, lang: string
}>) => {


interface MyComponentProps {
children: React.ReactNode;
theme: Theme;
lang: string
}

const MyComponent = (props: MyComponentProps) => {

Destructure props in function body

  • always destructure component props inside the function body for readability

const MyComponent = ({ children, theme, lang }: MyComponentProps) => {...}


const MyComponent = (props: MyComponentProps) => {
const { children, lang, theme } = props
...
}