mirror of
				https://github.com/lingble/twenty.git
				synced 2025-11-03 22:27:57 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			223 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
---
 | 
						||
sidebar_position: 3
 | 
						||
sidebar_custom_props:
 | 
						||
  icon: TbPencil
 | 
						||
---
 | 
						||
 | 
						||
# Style guide
 | 
						||
 | 
						||
We define here the rules to follow when writing code.
 | 
						||
 | 
						||
Our goal is to have a consistent codebase, easy to read and easy to maintain. 
 | 
						||
 | 
						||
For this we prefer to tend towards being a bit more verbose than being too concise.
 | 
						||
 | 
						||
Always keep in mind that code is read more often than it is written, especially on an open source project, where anyone can contribute.
 | 
						||
 | 
						||
There are a lot of rules that are not defined here, but that are automatically checked by our linters.
 | 
						||
 | 
						||
## React
 | 
						||
 | 
						||
### Use functional components
 | 
						||
 | 
						||
Always use TSX functional components.
 | 
						||
 | 
						||
Do not use default import with const, because it's harder to read and harder to import with code completion.
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad, harder to read, harder to import with code completion
 | 
						||
const MyComponent = () => {
 | 
						||
  return <div>Hello World</div>;
 | 
						||
};
 | 
						||
 | 
						||
export default MyComponent;
 | 
						||
 | 
						||
// ✅ Good, easy to read, easy to import with code completion
 | 
						||
export function MyComponent() {
 | 
						||
  return <div>Hello World</div>;
 | 
						||
};
 | 
						||
```
 | 
						||
 | 
						||
### Props
 | 
						||
 | 
						||
Create the type of the props and call it `OwnProps` if there's no need to export it.
 | 
						||
 | 
						||
Use props destructuring.
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad, no type
 | 
						||
export function MyComponent(props) {
 | 
						||
  return <div>Hello {props.name}</div>;
 | 
						||
};
 | 
						||
 | 
						||
// ✅ Good, type
 | 
						||
type OwnProps = {
 | 
						||
  name: string;
 | 
						||
};
 | 
						||
 | 
						||
export function MyComponent({ name }: OwnProps) {
 | 
						||
  return <div>Hello {name}</div>;
 | 
						||
};
 | 
						||
```
 | 
						||
 | 
						||
#### Refrain from using `React.FC` or `React.FunctionComponent` to define prop types.
 | 
						||
 | 
						||
```tsx
 | 
						||
/* ❌ - Bad, defines the component type annotations with `FC`
 | 
						||
 *    - With `React.FC`, the component implicitly accepts a `children` prop
 | 
						||
 *      even if it's not defined in the prop type. This might not always be
 | 
						||
 *      desirable, especially if the component doesn't intend to render
 | 
						||
 *      children.
 | 
						||
 */
 | 
						||
const EmailField: React.FC<{
 | 
						||
  value: string;
 | 
						||
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
 | 
						||
```
 | 
						||
 | 
						||
```tsx
 | 
						||
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the 
 | 
						||
 *      component's props
 | 
						||
 *    - This method doesn't automatically include the children prop. If
 | 
						||
 *      you want to include it, you have to specify it in OwnProps.
 | 
						||
 */ 
 | 
						||
type OwnProps = {
 | 
						||
  value: string;
 | 
						||
};
 | 
						||
 | 
						||
function EmailField({ value }: OwnProps) {
 | 
						||
  return <TextInput value={value} disabled fullWidth />;
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
## JavaScript 
 | 
						||
 | 
						||
### Use nullish-coalescing operator `??`
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad, can return 'default' even if value is 0 or ''
 | 
						||
const value = process.env.MY_VALUE || 'default';
 | 
						||
 | 
						||
// ✅ Good, will return 'default' only if value is null or undefined
 | 
						||
const value = process.env.MY_VALUE ?? 'default';
 | 
						||
```
 | 
						||
 | 
						||
### Use optional chaining `?.`
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad 
 | 
						||
onClick && onClick();
 | 
						||
 | 
						||
// ✅ Good
 | 
						||
onClick?.();
 | 
						||
```
 | 
						||
 | 
						||
## TypeScript
 | 
						||
 | 
						||
### Use type instead of Interface
 | 
						||
 | 
						||
We decided to always use type instead of interface, because they almost always overlap, and type is more flexible.
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad
 | 
						||
interface MyInterface {
 | 
						||
  name: string;
 | 
						||
}
 | 
						||
 | 
						||
// ✅ Good
 | 
						||
type MyType = {
 | 
						||
  name: string;
 | 
						||
};
 | 
						||
```
 | 
						||
 | 
						||
### Use string literals instead of enums
 | 
						||
 | 
						||
[String literals](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) are the go-to way to handle enum-like values in TypeScript. They are easier to extend with Pick and Omit, and offer a better developer experience, especially with code completion.
 | 
						||
 | 
						||
You can see why TypeScript recommend avoiding enums here : https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums
 | 
						||
 | 
						||
However, GraphQL codegen will generate enums, so you can't avoid them completely, but avoid creating new ones.
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad, utilizes an enum
 | 
						||
enum Color {
 | 
						||
  Red = "red",
 | 
						||
  Green = "green",
 | 
						||
  Blue = "blue",
 | 
						||
}
 | 
						||
 | 
						||
let color = Color.Red;
 | 
						||
```
 | 
						||
 | 
						||
```tsx
 | 
						||
// ✅ Good, utilizes a string literal
 | 
						||
let color: "red" | "green" | "blue" = "red";
 | 
						||
```
 | 
						||
 | 
						||
## Styling
 | 
						||
 | 
						||
### Use StyledComponents
 | 
						||
 | 
						||
Components should be styled with [styled-components](https://emotion.sh/docs/styled).
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad
 | 
						||
<div className="my-class">Hello World</div>
 | 
						||
```
 | 
						||
 | 
						||
```tsx
 | 
						||
// ✅ Good
 | 
						||
const StyledTitle = styled.div`
 | 
						||
  color: red;
 | 
						||
`;
 | 
						||
```
 | 
						||
 | 
						||
Styled components should be prefixed with "Styled" to differentiate them from "real" components.
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad
 | 
						||
const Title = styled.div`
 | 
						||
  color: red;
 | 
						||
`;
 | 
						||
```
 | 
						||
 | 
						||
```tsx
 | 
						||
// ✅ Good
 | 
						||
const StyledTitle = styled.div`
 | 
						||
  color: red;
 | 
						||
`;
 | 
						||
```
 | 
						||
 | 
						||
### Theming
 | 
						||
 | 
						||
Utilizing the theme for the majority of component styling is the preferred approach.
 | 
						||
 | 
						||
#### Units of measurement
 | 
						||
 | 
						||
Avoid using `px` or `rem` values directly within the styled components. The necessary values are generally already defined in the theme, so it’s recommended to make use of the theme for these purposes.
 | 
						||
 | 
						||
#### Colors
 | 
						||
 | 
						||
Refrain from introducing additional colors; instead, utilize the existing palette from the theme. Should there be a situation where the palette does not align, kindly leave a comment so that it can be rectified.
 | 
						||
 | 
						||
 | 
						||
```tsx
 | 
						||
// ❌ Bad, directly specifies style values without utilizing the theme
 | 
						||
const StyledButton = styled.button`
 | 
						||
  color: #333333;
 | 
						||
  font-size: 1rem;
 | 
						||
  font-weight: 400;
 | 
						||
  margin-left: 4px;
 | 
						||
  border-radius: 50px;
 | 
						||
`;
 | 
						||
```
 | 
						||
 | 
						||
```tsx
 | 
						||
// ✅ Good, utilizes the theme
 | 
						||
const StyledButton = styled.button`
 | 
						||
  color: ${({ theme }) => theme.font.color.primary};
 | 
						||
  font-size: ${({ theme }) => theme.font.size.md};
 | 
						||
  font-weight: ${({ theme }) => theme.font.weight.regular};
 | 
						||
  margin-left: ${({ theme }) => theme.spacing(1)};
 | 
						||
  border-radius:  ${({ theme }) => theme.border.rounded};
 | 
						||
`;
 |