Migrate twenty ui to jotai (#17900)

## Remove Recoil from twenty-ui

Completely removes the `recoil` dependency from `twenty-ui` by
converting all atoms, hooks, and providers to Jotai equivalents.

### twenty-ui
- `createState` now returns a Jotai `PrimitiveAtom` instead of a Recoil
atom
- `iconsState`, `IconsProvider`, `useIcons` converted to Jotai
(`useSetAtom`, `useAtomValue`)
- `RecoilRootDecorator` now uses Jotai `Provider` (name kept for compat)
- Deleted unused `invalidAvatarUrlsState` (Avatar already uses
`invalidAvatarUrlsAtomV2`)
- Removed `recoil` from `package.json`

### twenty-front
- Created local Recoil `createState` at
`@/ui/utilities/state/utils/createState` for ~112 state files still on
Recoil
- Updated all imports accordingly
- Removed `iconsState` from Recoil snapshot preservation in `useAuth`
(lives in Jotai store now)
This commit is contained in:
Charles Bochet
2026-02-12 16:43:34 +01:00
committed by GitHub
parent 09e48addb2
commit 310d13fd17
139 changed files with 165 additions and 168 deletions
@@ -1,5 +1,5 @@
import { type FieldFilesValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export const filePreviewState = createState<FieldFilesValue | null>({
key: 'filePreviewState',
@@ -1,4 +1,4 @@
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
import { IconApps, type IconComponent } from 'twenty-ui/display';
type IconPickerState = {
@@ -1,4 +1,4 @@
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export const activeDropdownFocusIdState = createState<string | null>({
key: 'activeDropdownFocusIdState',
@@ -1,4 +1,4 @@
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export const previousDropdownFocusIdState = createState<string | null>({
key: 'previousDropdownFocusIdState',
@@ -1,4 +1,4 @@
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export const navigationMemorizedUrlState = createState<string>({
key: 'navigationMemorizedUrlState',
defaultValue: '/',
@@ -1,4 +1,4 @@
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export const shouldNavigateBackToMemorizedUrlOnSaveState = createState<boolean>(
{
@@ -1,4 +1,4 @@
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export type StepsState = {
activeStep: number;
};
@@ -1,5 +1,5 @@
import { type FocusStackItem } from '@/ui/utilities/focus/types/FocusStackItem';
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export const focusStackState = createState<FocusStackItem[]>({
key: 'focusStackState',
@@ -1,5 +1,5 @@
import { type Keys } from 'react-hotkeys-hook/dist/types';
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export const pendingHotkeyState = createState<Keys | null>({
key: 'pendingHotkeyState',
@@ -1,4 +1,4 @@
import { createState } from 'twenty-ui/utilities';
import { createState } from '@/ui/utilities/state/utils/createState';
export const currentPageLocationState = createState<string>({
key: 'currentPageLocationState',
defaultValue: '',
@@ -0,0 +1,18 @@
import { atom, type AtomEffect } from 'recoil';
export const createState = <ValueType>({
key,
defaultValue,
effects,
}: {
key: string;
defaultValue: ValueType;
effects?: ReadonlyArray<AtomEffect<ValueType>>;
}) => {
const recoilState = atom<ValueType>({
key,
default: defaultValue,
effects,
});
return recoilState;
};