diff --git a/docs/docs/developer/additional/_category_.json b/docs/docs/developer/additional/_category_.json
index f1d8dba14..6827d6bba 100644
--- a/docs/docs/developer/additional/_category_.json
+++ b/docs/docs/developer/additional/_category_.json
@@ -1,6 +1,6 @@
{
"label": "Additional resources",
- "position": 3,
+ "position": 10,
"collapsible": true,
"collapsed": true,
"className": "navbar-sub-menu"
diff --git a/docs/docs/developer/frontend/_category_.json b/docs/docs/developer/frontend/_category_.json
index 7ec594c80..ade306b5b 100644
--- a/docs/docs/developer/frontend/_category_.json
+++ b/docs/docs/developer/frontend/_category_.json
@@ -1,7 +1,7 @@
{
"label": "Frontend",
"position": 3,
- "collapsible": false,
+ "collapsible": true,
"collapsed": false,
"className": "navbar-sub-menu"
}
\ No newline at end of file
diff --git a/docs/docs/developer/frontend/best-practices.mdx b/docs/docs/developer/frontend/best-practices.mdx
new file mode 100644
index 000000000..162cf8e13
--- /dev/null
+++ b/docs/docs/developer/frontend/best-practices.mdx
@@ -0,0 +1,151 @@
+---
+sidebar_position: 2
+sidebar_custom_props:
+ icon: TbChecklist
+---
+
+# Best practices
+
+## State management
+
+We use React and Recoil for state management.
+
+### Use `useRecoilState` to store state
+
+We recommend that you create as many atoms as you need to store your state.
+
+Rule of thumb : It's better to be using too many atoms than trying to be too concise with props drilling.
+
+```tsx
+export const myAtomState = atom({
+ key: 'myAtomState',
+ default: 'default value',
+});
+
+export function MyComponent() {
+ const [myAtom, setMyAtom] = useRecoilState(myAtomState);
+
+ return (
+
+ setMyAtom(e.target.value)}
+ />
+
+ );
+}
+```
+
+### Do not use `useRef` to store state
+
+We do not recommend using `useRef` to store state.
+
+If you want to store state, you should use `useState` or `useRecoilState`.
+
+We recommand seeing [how to manage re-renders](#managing-re-renders) if you feel like you need `useRef` to prevent some re-renders from happening.
+
+## Managing re-renders
+
+Re-renders can be hard to manage in React.
+
+We provide you with some rules that we follow to avoid unnecessary re-renders.
+
+Keep in mind that re-renders can **always** be avoided by understanding the cause of the re-render.
+
+### Work at the root level
+
+We made it easy for you to avoid re-renders in new features by taking care of eliminating them at the root level.
+
+There's only one `useEffect` in the sidecar component `AuthAutoRouter` that is holding all the logic that should be executed on a page change.
+
+That way you know that there's only one place that can trigger a re-render.
+
+### Always think twice before adding `useEffect` in your codebase
+
+Re-renders are often caused by unnecessary `useEffect`.
+
+You should think whether the useEffect is really needed, or if you can move the logic in a event handler function.
+
+You'll find it generally easy to move the logic in a `handleClick` or `handleChange` function.
+
+You can also find them in libraries like Apollo : `onCompleted`, `onError`, etc.
+
+### Use a sibling component to extract useEffect or data fetching logic
+
+If you feel like you need to add a `useEffect` in your root component, you should consider extracting it in a sidecar component.
+
+The same can be applied for data fetching logic, with Apollo hooks.
+
+```tsx
+// ❌ Bad, will cause re-renders even if data is not changing,
+// because useEffect needs to be re-evaluated
+export function PageComponent() {
+ const [data, setData] = useRecoilState(dataState);
+ const [someDependency] = useRecoilState(someDependencyState);
+
+ useEffect(() => {
+ if(someDependency !== data) {
+ setData(someDependency);
+ }
+ }, [someDependency]);
+
+ return
{data}
;
+};
+
+export function App() {
+ return (
+
+
+
+ );
+}
+```
+
+```tsx
+// ✅ Good, will not cause re-renders if data is not changing,
+// because useEffect is re-evaluated in another sibling component
+export function PageComponent() {
+ const [data, setData] = useRecoilState(dataState);
+
+ return
{data}
;
+};
+
+export function PageData() {
+ const [data, setData] = useRecoilState(dataState);
+ const [someDependency] = useRecoilState(someDependencyState);
+
+ useEffect(() => {
+ if(someDependency !== data) {
+ setData(someDependency);
+ }
+ }, [someDependency]);
+
+ return <>>;
+};
+
+export function App() {
+ return (
+
+
+
+
+ );
+}
+```
+
+### Use recoil family states and recoil family selectors
+
+Recoil family states and selectors are a great way to avoid re-renders.
+
+They are especially useful when you need to store a list of items.
+
+### You shouldn't use `React.memo(MyComponent)`
+
+We do not recommend `React.memo()` usage because it does not solve the cause of the re-render, but instead breaks the re-render chain, which can lead to unexpected behavior and make the code really hard to refactor.
+
+### Limit `useCallback` or `useMemo` usage
+
+They are often not necessary and will make the code harder to read and maintain for a gain of performance that is unnoticeable.
+
+
+
diff --git a/docs/docs/developer/frontend/design-system.mdx b/docs/docs/developer/frontend/design-system.mdx
new file mode 100644
index 000000000..6cbb1620d
--- /dev/null
+++ b/docs/docs/developer/frontend/design-system.mdx
@@ -0,0 +1,10 @@
+---
+sidebar_position: 6
+sidebar_custom_props:
+ icon: TbPaint
+---
+
+# Design System
+
+We rely on our internal and custom design system, that is built on top of styled-components.
+
diff --git a/docs/docs/developer/frontend/folder-architecture.mdx b/docs/docs/developer/frontend/folder-architecture.mdx
index a78143fe8..711453bb1 100644
--- a/docs/docs/developer/frontend/folder-architecture.mdx
+++ b/docs/docs/developer/frontend/folder-architecture.mdx
@@ -1,5 +1,5 @@
---
-sidebar_position: 0
+sidebar_position: 4
sidebar_custom_props:
icon: TbFolder
---
diff --git a/docs/docs/developer/frontend/getting-started.mdx b/docs/docs/developer/frontend/getting-started.mdx
new file mode 100644
index 000000000..c8279ce30
--- /dev/null
+++ b/docs/docs/developer/frontend/getting-started.mdx
@@ -0,0 +1,27 @@
+---
+sidebar_position: 1
+sidebar_custom_props:
+ icon: TbZoomQuestion
+---
+
+# Getting started
+
+## Installation
+
+To install the front end, you'll have to [install Yarn](https://yarnpkg.com/getting-started/install) first.
+
+Then just run :
+
+```
+yarn
+```
+
+## Development flow
+
+You can start the application for local development with :
+
+```
+yarn start
+```
+
+Then go to our [best-practice](/developer/frontend/best-practices) guide and the [folder architecture](/developer/frontend/folder-architecture) to learn more about how to structure your feature.
diff --git a/docs/docs/developer/frontend/hotkeys.mdx b/docs/docs/developer/frontend/hotkeys.mdx
new file mode 100644
index 000000000..1ac3cc442
--- /dev/null
+++ b/docs/docs/developer/frontend/hotkeys.mdx
@@ -0,0 +1,24 @@
+---
+sidebar_position: 10
+sidebar_custom_props:
+ icon: TbKeyboard
+---
+
+# Hotkeys
+
+You can intercept any hotkey combination and execute a custom action.
+
+We added a thin wrapper on top of [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) to make it more performant and to avoid unnecessary re-renders.
+
+We also created a wrapper hook `useScopedHotkeys` to make it easy to manage scopes.
+
+```ts
+useScopedHotkeys(
+ 'ctrl+k,meta+k',
+ () => {
+ openCommandMenu();
+ },
+ AppHotkeyScope.CommandMenu,
+ [openCommandMenu],
+);
+```
diff --git a/docs/docs/developer/frontend/overview.mdx b/docs/docs/developer/frontend/overview.mdx
new file mode 100644
index 000000000..6aef07777
--- /dev/null
+++ b/docs/docs/developer/frontend/overview.mdx
@@ -0,0 +1,52 @@
+---
+sidebar_position: 0
+sidebar_custom_props:
+ icon: TbEyeglass
+---
+
+# Overview
+
+## Tech Stack
+
+We took care of having a clean and simple stack, with minimal boilerplate code.
+
+**App**
+
+- [React](https://react.dev/)
+- [Apollo](https://www.apollographql.com/docs/)
+- [GraphQL Codegen](https://the-guild.dev/graphql/codegen)
+- [Recoil](https://recoiljs.org/docs/introduction/core-concepts)
+- [TypeScript](https://www.typescriptlang.org/)
+
+**Testing**
+
+- [Jest](https://jestjs.io/)
+- [Storybook](https://storybook.js.org/)
+
+**Tooling**
+
+- [Yarn](https://yarnpkg.com/)
+- [Craco](https://craco.js.org/docs/)
+- [ESLint](https://eslint.org/)
+
+## Architecture
+
+### Routing
+
+We use [React Router](https://reactrouter.com/) for routing.
+
+To avoid unnecessary [re-renders](/developer/frontend/best-practices#managing-re-renders) we handle all the routing logic in a `useEffect` in `AuthAutoRouter`.
+
+### State Management
+
+We use [Recoil](https://recoiljs.org/docs/introduction/core-concepts) for state management.
+
+See our [best practices](/developer/frontend/best-practices#state-management) for more managing state.
+
+## Testing
+
+We use [Jest](https://jestjs.io/) for unit testing and [Storybook](https://storybook.js.org/) for component testing.
+
+Jest is mainly used for testing utility functions, and not components themselves.
+
+Storybook is used for testing the behavior of isolated components, as well as displaying our [design system](/developer/frontend/design-system).
diff --git a/docs/docs/developer/frontend/style-guide.mdx b/docs/docs/developer/frontend/style-guide.mdx
new file mode 100644
index 000000000..35c34a915
--- /dev/null
+++ b/docs/docs/developer/frontend/style-guide.mdx
@@ -0,0 +1,102 @@
+---
+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
Hello World
;
+};
+
+export default MyComponent;
+
+// ✅ Good, easy to read, easy to import with code completion
+export function MyComponent() {
+ return
Hello World
;
+};
+```
+
+### 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
Hello {props.name}
;
+};
+
+// ✅ Good, type
+type OwnProps = {
+ name: string;
+};
+
+export function MyComponent({ name }: OwnProps) {
+ return
Hello {name}
;
+};
+```
+
+## 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;
+};
+```
+
diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
index 2f31539a8..ba194dfb6 100644
--- a/docs/docusaurus.config.js
+++ b/docs/docusaurus.config.js
@@ -8,7 +8,7 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula');
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Twenty - Documentation',
- tagline: 'Dinosaurs are cool',
+ tagline: 'Twenty is cool',
favicon: 'img/logo-square-dark.ico',
// Prevent search engines from indexing the doc for selected environments
diff --git a/docs/src/theme/DocSidebarItem/Link/index.js b/docs/src/theme/DocSidebarItem/Link/index.js
index f8a7e2c98..afedfb257 100644
--- a/docs/src/theme/DocSidebarItem/Link/index.js
+++ b/docs/src/theme/DocSidebarItem/Link/index.js
@@ -23,6 +23,12 @@ import {
TbBugOff,
TbBrandVscode,
TbFolder,
+ TbEyeglass,
+ TbZoomQuestion,
+ TbPaint,
+ TbChecklist,
+ TbKeyboard,
+ TbPencil,
} from "react-icons/tb";
@@ -53,6 +59,13 @@ export default function DocSidebarItemLink({
'TbBrandVscode': TbBrandVscode,
'TbDeviceDesktop': TbDeviceDesktop,
'TbFolder': TbFolder,
+ 'TbEyeglass': TbEyeglass,
+ 'TbZoomQuestion': TbZoomQuestion,
+ 'TbPaint': TbPaint,
+ 'TbChecklist': TbChecklist,
+ 'TbKeyboard': TbKeyboard,
+ 'TbChecklist': TbChecklist,
+ 'TbPencil': TbPencil,
};
let IconComponent = customProps && customProps.icon ? icons[customProps.icon] : TbFaceIdError;